引子:
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,[])。它可以確定任何對象的特定類型。