typeof 能夠判斷哪些類型?instanceof 作了什麼?null爲何被typeof錯誤的判斷爲了'object'

1、typeof

typeof 操做符惟一的目的就是檢查數據類型php

類型 typeof 結果
基本類型 undefined "undefined"
Boolean "boolean"
Number "number"
String "string"
BigInt (ECMAScript 2020 新增) "bigint"
Symbol "symbol"
null "object"
引用類型 Object(Object、Array、Map、Set等) "object"
Function "function"

因此,但咱們使用 typeof 來判斷引用類型變量時,不管是什麼類型的變量,它都會返回 Object 。html

爲此,引入了instanceof前端

2、instanceof

instanceoftypeof 相比,instanceof 方法要求開發者明確的確認對象爲某特定類型。即 instanceof 用於判斷引用類型屬於哪一個構造函數的方法。git

var arr = []
arr instanceof Array // true
typeof arr // "object"
// typeof 是沒法判斷類型是否爲數組的

instanceof 操做符檢測過程當中也會將繼承關係考慮在內,因此instanceof 能夠在繼承關係中用來判斷一個實例是否屬於它的父類型。github

// 判斷 f 是不是 Foo 類的實例 , 而且是不是其父類型的實例
function Aoo(){} 
function Foo(){} 
//JavaScript 原型繼承
Foo.prototype = new Aoo();
 
var foo = new Foo(); 
console.log(foo instanceof Foo) // true 
console.log(foo instanceof Aoo) // true

f instanceof Foo 的判斷邏輯是:面試

  • f 的 __proto__一層一層往上,是否對應到 Foo.prototype
  • 再往上,看是否對應着Aoo.prototype
  • 再試着判斷 f instanceof Object

instanceof 能夠用於判斷多層繼承關係。算法

3、instanceof 的內部實現原理

instanceof 的內部實現機制是:經過判斷對象的原型鏈上是否能找到對象的 prototype,來肯定 instanceof 返回值數組

1. 內部實現原理

// instanceof 的內部實現 
function instance_of(L, R) {//L 表左表達式,R 表示右表達式,即L爲變量,R爲類型
  // 取 R 的顯示原型
  var prototype = R.prototype
  // 取 L 的隱式原型
  L = L.__proto__
  // 判斷對象(L)的類型是否嚴格等於類型(R)的顯式原型
  while (true) { 
    if (L === null) {
      return false
    }
   
    // 這裏重點:當 prototype 嚴格等於 L 時,返回 true
    if (prototype === L) {
      return true
    } 
 
    L = L.__proto__
  } 
}

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

看下面一個例子,instanceof 爲何會返回 true?很顯然,an 並非經過 Bottle() 建立的。ecmascript

function An() {}
function Bottle() {}
An.prototype = Bottle.prototype = {};

let an = new An();
console.log(an instanceof Bottle); // true

這是由於 instanceof 關心的並非構造函數,而是原型鏈。

an.__proto__ === An.prototype; // true
An.prototype === Bottle.prototype; // true
// 即
an.__proto__ === Bottle.prototype; // true

即有 an.__proto__ === Bottle.prototype 成立,因此 an instanceof Bottle 返回了 true

因此,按照 instanceof 的邏輯,真正決定類型的是 prototype,而不是構造函數。

2. Object.prototype.toString(擴展)

還有一個不錯的判斷類型的方法,就是 Object.prototype.toString ,咱們能夠利用這個方法來對一個變量的類型來進行比較準確的判斷

默認狀況下(不覆蓋 toString 方法前提下),任何一個對象調用 Object 原生的 toString 方法都會返回 "[object type]",其中 type 是對象的類型;

let obj = {};

console.log(obj); // {}
console.log(obj.toString()); // "[object Object]"
[[Class]]

每一個實例都有一個 [[Class]] 屬性,這個屬性中就指定了上述字符串中的 type (構造函數名)。 [[Class]] 不能直接地被訪問,但一般能夠間接地經過在這個值上借用默認的 Object.prototype.toString.call(..) 方法調用來展現。

Object.prototype.toString.call("abc"); // "[object String]"
Object.prototype.toString.call(100); // "[object Number]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call([1,2,3]); // "[object Array]"
Object.prototype.toString.call(/\w/); // "[object RegExp]"

能夠經過 Object.prototype.toString.call(..) 來獲取每一個對象的類型。

function isFunction(value) {
  return Object.prototype.toString.call(value) === "[object Function]"
}
function isDate(value) {
  return Object.prototype.toString.call(value) === "[object Date]"
}
function isRegExp(value) {
  return Object.prototype.toString.call(value) === "[object RegExp]"
}

isDate(new Date()); // true
isRegExp(/\w/); // true
isFunction(function(){}); //true

或者可寫爲:

function generator(type){
  return function(value){
    return Object.prototype.toString.call(value) === "[object "+ type +"]"
  }
}

let isFunction = generator('Function')
let isArray = generator('Array');
let isDate = generator('Date');
let isRegExp = generator('RegExp');

isArray([]));    // true
isDate(new Date()); // true
isRegExp(/\w/); // true
isFunction(function(){}); //true
Symbol.toStringTag

Object.prototype.toString 方法可使用 Symbol.toStringTag 這個特殊的對象屬性進行自定義輸出。

舉例說明:

let bottle = {
  [Symbol.toStringTag]: "Bottle"
};

console.log(Object.prototype.toString.call(bottle)); // [object Bottle]

大部分和環境相關的對象也有這個屬性。如下輸出可能因瀏覽器不一樣而異:

// 環境相關對象和類的 toStringTag:
console.log(window[Symbol.toStringTag]); // Window
console.log(XMLHttpRequest.prototype[Symbol.toStringTag]); // XMLHttpRequest

console.log(Object.prototype.toString.call(window)); // [object Window]
console.log(Object.prototype.toString.call(new XMLHttpRequest())); // [object XMLHttpRequest]

輸出結果和 Symbol.toStringTag(前提是這個屬性存在)同樣,只不過被包裹進了 [object ...] 裏。

因此,若是但願以字符串的形式獲取內置對象類型信息,而不只僅只是檢測類型的話,能夠用這個方法來替代 instanceof

3. 總結

適用於 返回
typeof 基本數據類型 string
instanceof 任意對象 true/false
Object.prototype.toString 基本數據類型、內置對象以及包含 Symbol.toStringTag 屬性的對象 string

Object.prototype.toString 基本上就是一加強版 typeof

instanceof 在涉及多層類結構的場合中比較實用,這種狀況下須要將類的繼承關係考慮在內。

4、null爲何被typeof錯誤的判斷爲了'object'

// JavaScript 誕生以來便如此
typeof null === 'object';

在 JavaScript 最初的實現中,JavaScript 中的值是由一個表示類型的標籤和實際數據值表示的。對象的類型標籤是 0。因爲 null 表明的是空指針(大多數平臺下值爲 0x00),所以,null 的類型標籤是 0,typeof null 也所以返回 "object"。(參考來源

曾有一個 ECMAScript 的修復提案(經過選擇性加入的方式),但被拒絕了。該提案會致使 typeof null === 'null'

若是用 instanceof 來判斷的話:

null instanceof null
// Uncaught TypeError: Right-hand side of 'instanceof' is not an object

天天三分鐘,進階一個前端小 tip 面試題庫 算法題庫

相關文章
相關標籤/搜索