第4章 類型判斷(1 / 3)

引子:

JS 有 8 種數據類型,那我們如何判斷不同的數據類型,進行對應的數據處理呢?

常見的有 typeof, instanceof, constructor, Object.prototype.toString.call,Reflect.apply(Object.prototype.toString, val, [])。

第一種常用於檢測 基本類型和 Function,除 null 外。

第二三種常用於檢測 引用類型,無法區分基本類型。

第四五種常用於檢測 基本類型和引用類型。

同時,我們擴展延伸一下,深淺拷貝的相關知識:定義,如何實現一個深淺拷貝,什麼時候用深拷貝、什麼時候用淺拷貝。

1. typeof 關鍵字

1-1.語法

typeof operand typeof(operand)

operand: 一個表示對象或原始值的表達式,其類型將被返回。

返回值:字符串

1-2.優點

用於區分基本數據類型,但無法區分null

1-3.缺點

無法區分引用數據類型,但能區分function

1-4.代碼實踐

// 基本數據類型

console.log(typeof undefined) // \u0027undefined\u0027

console.log(typeof 2) // \u0027number\u0027

console.log(typeof true) // \u0027boolean\u0027

console.log(typeof \u0027str\u0027) // \u0027string\u0027

console.log(typeof Symbol(\u0027a\u0027)) // \u0027symbol\u0027

console.log(typeof 1n) // \u0027bigint\u0027

console.log(typeof null) // \u0027object\u0027

// 引用數據類型

console.log(typeof []) // \u0027object\u0027

console.log(typeof {}) // \u0027object\u0027

console.log(typeof new Date()) // \u0027object\u0027

console.log(typeof function () {}) // \u0027function\u0027

// 有趣的,要注意

console.log(typeof Infinity) // \u0027number\u0027

console.log(typeof NaN) // \u0027number\u0027

console.log(typeof Math.LN2) // \u0027number\u0027

console.log(typeof new Number(1)) // \u0027object\u0027

console.log(typeof Number(\u00271\u0027)) // \u0027number\u0027

console.log(typeof typeof 1) // \u0027string\u0027

// typeof 一個不存在的變量,不會報錯,隻會返回\u0027undefined\u0027

console.log(typeof smallAJ) // \u0027undefined\u0027

typeof Object // \u0027function\u0027

const fn \u003d () \u003d> {}

typeof fn // \u0027function\u0027

1-5.原理

// 第一版JS實現

if (JSVAL_IS_VOID(v)) { // (1)判斷是否為 undefined

type \u003d JSTYPE_VOID;

} else if (JSVAL_IS_OBJECT(v)) { // (2)判斷是否為對象

obj \u003d JSVAL_TO_OBJECT(v);

if (obj \u0026\u0026

(ops \u003d obj - > map - > ops,

ops \u003d\u003d \u0026 js_ObjectOps ?

(clasp \u003d OBJ_GET_CLASS(cx, obj),

clasp - > call || clasp \u003d\u003d \u0026 js_FunctionClass) // (3,4)

:

ops - > call !\u003d 0)) { // (3)判斷是否為數字

type \u003d JSTYPE_FUNCTION;

} else {

type \u003d JSTYPE_OBJECT;

}

} else if (JSVAL_IS_NUMBER(v)) {

type \u003d JSTYPE_NUMBER;

} else if (JSVAL_IS_STRING(v)) {

type \u003d JSTYPE_STRING;

} else if (JSVAL_IS_BOOLEAN(v)) {

type \u003d JSTYPE_BOOLEAN;

}

2. instanceof 關鍵字 constructor 屬性

2-1.語法

object instanceof constructor

object: 某個實例對象

constructor: 某個構造函數

instanceof 運算符用來 在運行時 檢測 constructor.prototype 是否存在於參數 object 的原型鏈上。

左側應是對象,若不是,直接返回 false。

非標準的__proto__ 偽屬性 可以改變對象的原型,此處例如Parent.__proto__\u003dA 則原圖會指向 A 函數 prototype: 顯式原型__proto__: 隱式原型

2-2.優點

用於區分引用數據類型

2-3.缺點

不能用來檢測基本數據類型

2-4.代碼實踐

// 基本數據類型

let number \u003d 123

let string \u003d \u0027aj\u0027

let boolean \u003d false

let symbol \u003d Symbol(\u0027symbol\u0027)

let bigint \u003d BigInt(10)

console.log(number instanceof Number) // false

console.log(string instanceof String) // false

console.log(boolean instanceof Boolean) // false

console.log(symbol instanceof Symbol) // false

console.log(bigint instanceof BigInt) // false

// TypeError: Right-hand side of \u0027instanceof\u0027 is not an object

console.log(null instanceof null)

// TypeError: Right-hand side of \u0027instanceof\u0027 is not an object

console.log(undefined instanceof undefined)

// 引用數據類型

let newNumber \u003d new Number(123)

let newString \u003d new String(\u0027aj\u0027)

let newBoolean \u003d new Boolean(false)

function Fn() {}

let date \u003d new Date()

let array \u003d []

let obj \u003d {} // 字麵量是直接生成構造函數的

console.log(newNumber instanceof Number) // true

console.log(newString instanceof String) // true

console.log(newBoolean instanceof Boolean) // true

console.log(Fn instanceof Function) // true

console.log(date instanceof Date) // true

console.log(array instanceof Array) // true

console.log(obj instanceof Object) // true

// 判斷是否為數組,建議使用 isArray()

console.log(Array.isArray(array)) // true

小測試,檢驗一下:

function Fn() {}

let obj \u003d new Fn()

console.log(obj instanceof Function) // false

console.log(obj instanceof Object) // true

console.log(Fn instanceof Function) // true

obj 是構造函數 new 出來的實例對象,所以 obj 的原型鏈上應該是Object。

2-5.原理

思路: 比較左邊 object 的proto 是否存在於 右邊 constructor 的原型鏈上, 左邊 object 查找到 null 位置。 手撕代碼如下:

/**

* ES5 ES6 模擬instanceof

* @author 阿吉

* @param {Object} object

* @param {Object} constructor

*/

function myInstanceofES5(object, constructor) {

var Prototype \u003d constructor.prototype // constructor 的顯式原型

object \u003d object.__proto__ // object 的隱式原型

while (true) {

if (object \u003d\u003d\u003d null) return false

if (object \u003d\u003d\u003d Prototype) return true

object \u003d object.__proto__

}

}

const myInstanceofES6 \u003d (object, constructor) \u003d>

object \u003d\u003d null

? false

: object \u003d\u003d constructor

? true

: myInstanceof(myInstanceof.__proto__, constructor)

既然typeof用來檢測 基本數據類型,instanceof用來檢測引用數據類型,那麼有沒有一種方法,既能檢測基本數據類型,又能檢測引用數據類型呢?答案就是Object.prototype.toString.call(val)或Reflect.apply(Object.prototype.toString,val,[])。它可以確定任何對象的特定類型。