首頁 > 軟體

分享JavaScript 型別判斷的幾種方法

2022-05-25 22:00:24

一、JavaScript 基本型別

首先介紹一下JavaScript中的八大基本型別

1、原始資料型別

除物件型別(object)以外的其它任何型別定義的不可變的值(值本身無法被改變)。例如(與 C 語言不同),JavaScript 中字串是不可變的(譯註:如,JavaScript 中對字串的操作一定返回了一個新字串,原始字串並沒有被改變)。我們稱這些型別的值為“原始值”。 —— MDN

(1)布林型別(Boolean)

布林表示一個邏輯實體,可以有兩個值:truefalse 、

(2)Null型別

  • Null 型別只有一個值:null
  • 值 null 是一個字面量,是表示缺少的標識,只是變數未指向任何物件。
  • 注意:可以把 null 作為尚未建立的物件,但是 null 並非物件。

(3)Undefined型別

  • undefined:一個宣告未定義的變數的初始值,或沒有實際引數的形式引數。
  • undefined全域性物件的一個屬性。也就是說,它是全域性作用域的一個變數。undefined的最初值就是原始資料型別undefined。
console.log(undefined in window)  //  true
let a;
console.log(a)  // undefined
  • 一個函數如果沒有使用return語句指定返回值,就會返回一個undefined值。
function f() {
}
console.log(f())  // undefined

(4)數位型別(Number)

  • 數位型別是一種基於 IEEE 754 標準的雙精度 64 位二進位制格式的值,能表示整數和浮點值
  • 值的範圍: Number.MIN_VALUE(5e-324)— Number.MAX_VALUE(1.7976931348623157e+308)
  • 如果某個正值大於 Number.MAX_VALUE,則表示為Infinity(或者+Infinity)
    • 如果某個負值小於 -Number.MAX_VALUE則表示為-Infinity
    • 如果某個正值小於 Number.MIN_VALUE,則表示為0(或者+0)
    • 如果某個負值大於 -Number.MIN_VALUE則表示為-0
// 注意:Number.MIN_VALUE(5e-324)— Number.MAX_VALUE(1.7976931348623157e+308) 是可以表示的正值範圍
console.log(Number.MIN_VALUE);  // 5e-324
console.log(Number.MAX_VALUE);  // 1.7976931348623157e+308

console.log(1e310);  // Infinity
console.log(-1e310);  // -Infinity

console.log(1e-330);  // 0
console.log(-1e-330);  // 0
  • NaN(非數值,Not-a-Number)。

not-a-number:NaN 是一個全域性物件的屬性。NaN 屬性的初始值就是 NaN,和 Number.NaN 的值一樣。

  • 數位型別中只有一個整數有兩種表示方法:0可表示為-0和+0(0是+0的簡寫)。

(5)BigInt

  • BigInt 型別是 JavaScript 中的一個基礎的數值型別,可以表示任意精度的整數。使用 BigInt,您可以安全地儲存和操作大整數,甚至可以超過數位型別安全整數限制。(BigInt 與 Number 的主要區別在於,BigInt能在記憶體中精確表示超過安全限制的整數)
// 數位型別安全整數限制
console.log(Number.MIN_SAFE_INTEGER)  // -9007199254740991
console.log(Number.MAX_SAFE_INTEGER)  // 9007199254740991
  • BigInt 是通過在整數末尾附加字母n或呼叫建構函式來建立的。
// 建構函式建立
// BigInt() 不與 new 運運算元一起使用。
let a = BigInt(1);
// 末尾附加字母n建立
let b = 1n;
console.log(a)  // 1n
console.log(b)  // 2n
  • BigInt不能與數位相互運算。否則,將丟擲TypeError
console.log(1 + 1n);
//Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

(6)字串型別(String)

JavaScript 的字串型別用於表示文字資料。它是一組 16 位的無符號整數值的“元素”。在字串中的每個元素佔據了字串的位置。第一個元素的索引為0,下一個是索引1,依此類推。字串的長度是它的元素的數量。

(7)符號型別(Symbols)

  • 符號(Symbols)型別是唯一不可修改的原始值,該型別的性質在於這個型別的值可以用來建立匿名的物件屬性,可以用來作為物件的鍵(key)
  • 可以通過呼叫函數 Symbol() 來建立 Symbol 資料型別範例。
  • 當一個 symbol 型別的值在屬性賦值語句中被用作識別符號,該屬性(像這個 symbol 一樣)是匿名的;並且是不可列舉的。因為這個屬性是不可列舉的,它不會在迴圈結構 “for( ... in ...)” 中作為成員出現,也因為這個屬性是匿名的,它同樣不會出現在 “Object.getOwnPropertyNames()” 的返回陣列裡。這個屬性可以通過建立時的原始 symbol 值存取到,或者通過遍歷 “Object.getOwnPropertySymbols() ” 返回的陣列。在之前的程式碼範例中,通過儲存在變數 myPrivateMethod的值可以存取到物件屬性。
  • symbol 值提供了一種自定義類可以建立私有成員的方式,並維護一個僅適用於該類的 symbol 登入檔。 自定義類可以使用 symbol 值來建立“自有”屬性,這些屬性避免了不必要的被偶然發現帶來的影響。 在類定義中,動態建立的 symbol 值將儲存到作用域變數中,該變數只能在類定義中私有地使用。

2、參照資料型別

物件型別(Object)

在電腦科學中, 物件(object)是指記憶體中的可以被識別符號參照的一塊區域。

在 JavaScript 中,物件可以被看作是一組屬性的集合。用物件字面量語法來定義一個物件時,會自動初始化一組屬性。而後,這些屬性還可以被新增和移除。

  • 屬性的值可以是任意型別,包括其它物件。
  • 屬性使用鍵(key)來標識,它的鍵值可以是一個字串或者符號值(Symbol)。

物件擁有兩種屬性:

a、資料屬性

b、存取器屬性

JavaScript中提供了很多內建物件,可參考MDN官方檔案

3、兩種資料型別的儲存方式

(1)原始資料型別

基礎型別儲存在棧記憶體,被參照或拷貝時,會建立一個完全相等的變數;佔據空間小、大小固定,屬於被頻繁使用資料,所以放入棧中儲存。

(2)參照資料型別

參照型別儲存在堆記憶體,儲存的是地址,多個參照指向同一個地址,這裡會涉及一個“共用”的概念;佔據空間大、大小不固定。參照資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。當直譯器尋找參照值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

所以,在 JavaScript 中,原始型別的賦值會完整複製變數值,而參照型別的賦值是複製參照地址。

二、Javascript 資料型別判斷的幾種方法

1、typeof

typeof操作符返回一個字串,表示未經計算的運算元的型別。

語法:

// operand:一個表示物件或原始值的表示式,其型別將被返回
typeof operand
typeof(operand)

返回值:

"undefined" "object"  "boolean"  "number" "bigint" "string" "symbol" "function" "object"

(1)不同資料型別的 typeof 值

// 歷史遺留問題
console.log(typeof null);  // object
// Undefined 型別typeof值為 undefined
console.log(typeof undefined);  // undefined
// 未定義的變數 typeof值也為 undefined
console.log(typeof a);  // undefined
// 非數值
console.log(typeof NaN);  // number
// 無窮大
console.log(typeof Infinity);  // number
// 除 Null 和 Undefined 之外的原始資料型別 typeof 值都是對應的型別
console.log(typeof true);  // boolean
console.log(typeof 2);  // number
console.log(typeof 2n);  // bigint
console.log(typeof BigInt(1));  // bigint
console.log(typeof 'str');  // string
console.log(typeof Symbol(1));  // symbol
console.log(typeof Boolean(true));  // boolean
console.log(typeof Number(1));  // number
console.log(typeof String('str'));  // string
// 基本包裝型別
console.log(typeof new Boolean(true));  // object
console.log(typeof new Number(1));  // object
console.log(typeof new String('str'));  // object
// 字面量
console.log(typeof {});  // object
console.log(typeof []);  // object
console.log(typeof /1/);  // object
// 除了 Function 物件範例, typeof值都是 object
console.log(typeof new Object());  // object
console.log(typeof new Array());  // object
console.log(typeof new Map());  // object
console.log(typeof new Set());  // object
console.log(typeof new Date());  // object
console.log(typeof new RegExp());  // object
console.log(typeof Math);  // object
// Function 物件範例, typeof值為 function
console.log(typeof new Function());  // function
console.log(typeof Function);  // function
console.log(typeof Array);  // function
console.log(typeof Set);  // function
console.log(typeof Date);  // function
console.log(typeof RegExp);  // function
console.log(typeof Object);  // function

(2) typeof null === 'object' 的原因

“typeof null”錯誤是 JavaScript 第一版的遺留物。在這個版本中,值儲存在 32 位單元中,由一個小型標籤(1-3 位)和值的實際資料組成。型別標籤儲存在單元的低位中。

其中有五個:

  • 000:物件。資料是對物件的參照。
  • 1:整數。資料是一個 31 位有符號整數。
  • 010:雙倍。資料是對雙浮點數的參照。
  • 100:字串。資料是對字串的參照。
  • 110:布林值。資料是布林值。

也就是說,最低位是任一位,那麼型別標籤只有一位長。或者為零,則型別標籤的長度為三位,為四種型別提供兩個額外的位。

有兩個值是特殊的:

  • undefined ( JSVAL_VOID ) 是整數 -2 30(整數範圍之外的數位)。
  • null ( JSVAL_NULL ) 是機器碼 NULL 指標。或者:一個物件型別標籤加上一個為零的參照。(表示為全0)

所以 typeof 認為 null 是一個 object

(3)物件型別 typeof 返回值的理解

針對物件型別,個人理解 的是:

  • 如果是Function物件, 那麼返回值則是function
  • 如果是其他任何普通物件, 那麼返回值則是object,

(4)關於 typeof Boolean(true) 和 typeof new Boolean(true)不同的原因

// 簡單呼叫Boolean() 函數
console.log(Boolean(true));  // true
// 建立一個Boolean物件
console.log(new Boolean(true));  // Boolean {true} 

Boolean(true) 的返回值就是Boolean型別的資料,newBoolean(true)返回的是一個Boolean物件。

(5)總結

  • typeof 可以判斷除 null 之外所有原始資料型別
  • typeof 在判斷範例型別時,根據範例的建構函式返回 object 或 function
  • typeof null === 'object' 是一個歷史遺留問題,無法修改。
  • typeof NaN === 'number'

2、instanceof

instanceof運運算元用於檢測建構函式的 prototype 屬性是否出現在某個範例物件的原型鏈

語法:

// object:某個範例物件
// constructor:某個建構函式
object instanceof constructor

(1)相關例子

// instanceof 右邊是一個建構函式,所以undefined 和 null 不能使用instanceof,下面兩條語句執行會報錯
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);
// 對於原始資料型別無法檢測
console.log(2 instanceof Number);  // false
console.log(1n instanceof BigInt);  // false
console.log(true instanceof Boolean);  // false 
console.log('str' instanceof String);  // false  
console.log(Symbol(1) instanceof Symbol);  // false

// 可以檢測內建物件型別
console.log([] instanceof Array);  // true
console.log(function(){} instanceof Function);  // true
console.log({} instanceof Object);  // true   
console.log(/1/ instanceof RegExp);  // true 
// 所有函數物件範例原型鏈上都存在Function
console.log(Array instanceof Function);  // true 
console.log(Set instanceof Function);  // true 
console.log(Map instanceof Function);  // true 
console.log(Date instanceof Function);  // true 
console.log(RegExp instanceof Function);  // true 
console.log(Function instanceof Function);  // true 
console.log(Object instanceof Function);  // true 
// Object 是所有範例物件原型鏈的盡頭
console.log(Array instanceof Object);  // true 
console.log(Set instanceof Object);  // true 
console.log(Map instanceof Object);  // true 
console.log(Date instanceof Object);  // true 
console.log(RegExp instanceof Object);  // true 
console.log(Function instanceof Object);  // true 
console.log(Object instanceof Object);  // true 
function foo() {}
// foo 是 Function的一個範例
console.log(foo instanceof Object);  // true
console.log(foo instanceof Function);  // true
// new foo() 是 foo 的一個範例
console.log(new foo() instanceof foo);  // true
console.log(new foo() instanceof Object);  // true
console.log(new foo() instanceof Function);  // false
// foo() 返回的是undefined
console.log(foo() instanceof Object);  // false
console.log(foo() instanceof Function);  // false

(2) 當建構函式的proptotype發生改變時,instanceof 結果可能會出錯

function foo() {}
let f = new foo();
console.log(f instanceof foo)  // true
// foo 原型發生改變
foo.prototype = Array.prototype;
console.log(f instanceof foo);  // false

(3)手動實現 instanceof

function myInstanceof(left, right) {
    // 獲得建構函式的原型
    let prototype = right.prototype
    // 獲得物件的原型
    left = left.__proto__
    // 判斷物件的型別是否等於型別的原型
    while (true) {
        // 原型鏈的盡頭是 null,說明範例物件的原型鏈遍歷結束
    	if (left === null)
    		return false
    	if (prototype === left)
    		return true
    	left = left.__proto__
    }
}

上述手動實現只是實現了基本功能,但與原生instanceof仍然存在差別,例如:

  • 未對 right 進行錯誤處理
// 檢驗right 是否是一個物件(Object)
if (right is not Object){
    throw new TypeError(" Uncaught TypeError: Right-hand side of 'instanceof' is not Object")
}
// 檢驗 right 是否可被呼叫
if (right is not callable) {
    throw new TypeError(" Uncaught TypeError: Right-hand side of 'instanceof' is not callable")
}
  • myInstanceof('str', String) === true
console.log(_instanceof('str', String))  // true

有關instanceof 原理,可繼續深入瞭解原型鏈相關知識點。

(4)總結

  • instanceof 不能判斷原始資料型別
  • instanceof 能夠判斷參照資料型別,但由於是查詢原型鏈,所以不能很精準指出資料型別。

例如:

console.log([] instanceof Array);  // true
console.log([] instanceof Object);  // true
  • 當建構函式的 prototype 發生改變時,會導致 instanceof 結果發生改變,詳見(2)

所以 instanceof 用於判斷資料型別也存在弊端。

3、建構函式(constructor)

(1)相關例子

// null 和 undefined 沒有建構函式
onsole.log(null.constructor)  // TypeError
onsole.log(undefined.constructor) // TypeError
// 原始資料型別
console.log((2).constructor === Number); // true
console.log((2n).constructor === BigInt); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(Symbol(1).constructor === Symbol); // true
// 字面量
console.log(([]).constructor === Array); // true
console.log((/1/).constructor === RegExp); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
// JavaScript中的內建函數物件的建構函式為 Function
console.log(Array.constructor === Function); // true
console.log(String.constructor === Function); // true
console.log(Function.constructor === Function); // true
console.log(Object.constructor === Function); // true
// JavaScript中的內建普通物件的建構函式為 Object
console.log(Math.constructor === Object); // true
console.log(JSON.constructor === Object); // true
console.log(Atomics.constructor === Object); // true
console.log(Intl.constructor === Object); // true
console.log(Reflect.constructor === Object); // true
console.log(WebAssembly.constructor === Object); // true

(2)如果建立一個物件前修改建構函式的原型,會導致constructor不可靠

function Fn(){};
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor===Fn);    // false
console.log(f.constructor===Array); // true 

(3)總結

  • 通過建構函式 constructor 能夠準確判斷出資料型別,但 null 和 undefined 沒有建構函式,無法判斷
  • 內建函數物件的建構函式都是 Function,例如 Array、Map等;內建普通物件的建構函式是Object,例如:JSON、Atomic、Intl、Reflect、WebAssembly 等
  • 在建立範例前修改範例的原型,會導致constructor不可靠

所以 constructor 判斷資料型別也存在弊端。

4、Object.prototype.toString.call()

(1)Object.prototype.toString()

每個物件都有一個toString()方法,當該物件被表示為一個文字值時,或者一個物件以預期的字串方式參照時自動呼叫。預設情況下,toString()方法被每個Object物件繼承。如果此方法在自定義物件中未被覆蓋,toString() 返回  "[object type]" ,其中type是物件的型別 —— MDN

(2) Object.prototype.toString.call() 範例

// 原始資料型別
console.log(Object.prototype.toString.call(undefined))  // [object Undefined]
console.log(Object.prototype.toString.call(null))  // [object Null]
console.log(Object.prototype.toString.call(1))  // [object Number]
console.log(Object.prototype.toString.call(1n))  // [object BigInt]
console.log(Object.prototype.toString.call(true))  // [object Boolean]
console.log(Object.prototype.toString.call('str'))  // [object String]
console.log(Object.prototype.toString.call(Symbol(1)))  // [object Symbol]
// 字面量
console.log(Object.prototype.toString.call({}))  // [object object]
console.log(Object.prototype.toString.call([]))  // [object Array]
console.log(Object.prototype.toString.call(/1/))  // [object RegExp]
console.log(Object.prototype.toString.call(NaN))  // [object Number]
console.log(Object.prototype.toString.call(Infinity))  // [object Number]
console.log(Object.prototype.toString.call(globalThis))  // [object Window]
// 內建函數物件範例
console.log(Object.prototype.toString.call(new Array()))  // [object Array]
console.log(Object.prototype.toString.call(new Map()))  // [object Map]
console.log(Object.prototype.toString.call(new Set()))  // [object Set]
console.log(Object.prototype.toString.call(new Date()))  // [object Date]
console.log(Object.prototype.toString.call(new Function()))  // [object Function]
// 內建普通物件本身
console.log(Object.prototype.toString.call(Math))  // [object Math]
console.log(Object.prototype.toString.call(JSON))  // [object JSON]
console.log(Object.prototype.toString.call(Reflect))  // [object Reflect]
// 內建函數物件本身
console.log(Object.prototype.toString.call(Array))  // [object Function]
console.log(Object.prototype.toString.call(Map))  // [object Function]
console.log(Object.prototype.toString.call(Function))  // [object Function]
console.log(Object.prototype.toString.call(Object))  // [object Function]
// 瀏覽器全域性物件
console.log(Object.prototype.toString.call(document))  // [object HTMLDocument]
console.log(Object.prototype.toString.call(window))  // [object Window]
// 自定義建構函式
function Foo() {}
f = new Foo();
console.log(Object.prototype.toString.call(f))  // [object object]
console.log(Object.prototype.toString.call(Foo))  // [object Function]

(3)基於Object.prototype.toString.call()實現資料型別判斷

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {    // 先進行typeof判斷,如果是基礎資料型別,直接返回
    return type;
  }
  // 對於typeof返回結果是object的,再進行如下的判斷,正則返回結果
  return Object.prototype.toString.call(obj).replace(/^[object (S+)]$/, '$1');  // 注意正則中間有個空格
}

(4)總結

Object.prototype.toString.call() 能準確判斷資料型別。

三、寫在最後

  • 除了 Null 之外的原始資料型別,均可使用 typeof 判斷資料型別。
  • Object.prototype.toString.call()  是判斷資料型別的最好的方法。
  • 有關 instanceof 和 constructor , 可以深入去了解JavaScript中的原型鏈。

到此這篇關於分享JavaScript 型別判斷的幾種方法的文章就介紹到這了,更多相關JavaScript 型別判斷內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com