JS-資料型別- typeof/instanceof/Object.prototype.toString

chrisghb發表於2018-12-01

參考文章:資料型別

typeof 運算子

JavaScript 有三種方法,可以確定一個值到底是什麼型別。

  • typeof運算子
  • instanceof運算子
  • Object.prototype.toString方法

typeof

typeof運算子可以返回一個值的資料型別。

  • 數值、字串、布林值分別返回numberstringboolean
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
複製程式碼
  • 函式返回function
function f() {}
typeof f
// "function"
複製程式碼
  • undefined返回undefined
typeof undefined
// "undefined"
複製程式碼

利用這一點,typeof可以用來檢查一個沒有宣告的變數,而不報錯

v
// ReferenceError: v is not defined

typeof v
// "undefined"
複製程式碼

上面程式碼中,變數v沒有用var命令宣告,直接使用就會報錯。但是,放在typeof後面,就不報錯了,而是返回undefined

實際程式設計中,這個特點通常用在判斷語句。

// 錯誤的寫法
if (v) {
  // ...
}
// ReferenceError: v is not defined

// 正確的寫法
if (typeof v === "undefined") {
  // ...
}
複製程式碼
  • 物件返回object
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
複製程式碼

typeof缺點:上面程式碼中,空陣列([])的型別也是object,這表示在 JavaScript 內部,陣列本質上只是一種特殊的物件。 typeof 無法區分陣列和物件。
這裡順便提一下,instanceof運算子可以區分陣列和物件

var o = {};
var a = [];

o instanceof Array // false
a instanceof Array // true
複製程式碼
  • null返回object。因為null被認為是一個空的物件的引用
typeof null // "object"
複製程式碼

instanceof

instanceof運算子返回一個布林值,表示物件是否為某個建構函式的例項

  • 下面程式碼中,物件v是建構函式Vehicle的例項,所以返回true
var v = new Vehicle();
v instanceof Vehicle // true
複製程式碼
  • instanceof運算子的左邊是例項物件,右邊是建構函式。
  • instanceof作用:會檢查右邊建構函式的原型物件(prototype),是否在左邊物件的原型鏈上。因此,下面兩種寫法是等價的。
v instanceof Vehicle
// 等同於
Vehicle.prototype.isPrototypeOf(v)
複製程式碼

上面程式碼中,Object.prototype.isPrototypeOf的含義如下:

var p = {x:1};//定義一個原型物件
var o = Object.create(p);//使用這個原型建立一個物件
p.isPrototypeOf(o);//=>true:o繼承p
Object.prototype.isPrototypeOf(p);//=> true p繼承自Object.prototype
複製程式碼
  • 由於instanceof檢查整個原型鏈,因此同一個例項物件,可能會對多個建構函式都返回true
var d = new Date();
d instanceof Date // true
d instanceof Object // true
複製程式碼

上面程式碼中,d同時是DateObject的例項,因此對這兩個建構函式都返回true

  • instanceof原理是檢查右邊建構函式的prototype屬性,是否在左邊物件的原型鏈上。有一種特殊情況,就是左邊物件的原型鏈上,只有null物件。這時,instanceof判斷會失真
var obj = Object.create(null);
typeof obj // "object"
Object.create(null) instanceof Object // false
複製程式碼

上面程式碼中,Object.create(null)返回一個新物件obj,它的原型是null(Object.create的詳細介紹見後文)。右邊的建構函式Objectprototype屬性,不在左邊的原型鏈上,因此instanceof就認為obj不是Object的例項。但是,只要一個物件的原型不是nullinstanceof運算子的判斷就不會失真。

  • instanceof運算子的一個用處,是判斷值的型別。
var x = [1, 2, 3];
var y = {};
x instanceof Array // true
y instanceof Object // true
複製程式碼

上面程式碼中,instanceof運算子判斷,變數x是陣列,變數y是物件。

缺點instanceof運算子只能用於物件(純物件和陣列),不適用原始型別(Undefined、Null、Boolean、Number 和 String)的值。

var s = 'hello';
s instanceof String // false
複製程式碼

上面程式碼中,字串不是String物件的例項(因為字串不是物件),所以返回false

此外,對於undefinednullinstanceOf運算子總是返回false

undefined instanceof Object // false
null instanceof Object // false
複製程式碼
  • 利用instanceof運算子,還可以巧妙地解決,呼叫建構函式時,忘了加new命令的問題。
function Fubar (foo, bar) {
  if (this instanceof Fubar) {
    this._foo = foo;
    this._bar = bar;
  } else {
    return new Fubar(foo, bar);
  }
}
複製程式碼

上面程式碼使用instanceof運算子,在函式體內部判斷this關鍵字是否為建構函式Fubar的例項。如果不是,就表明忘了加new命令。

Object.prototype.toString

  • 要想區別物件、陣列、函式單純使用 typeof 是不行的。nullArray 的結果也是 object,有時候我們需要的是 "純粹" 的 object 物件。
  • 我們可以通過Object.prototype.toString方法準確判斷某個物件值屬於哪種內建型別。在介紹Object.prototype.toString方法之前,我們先把toString()方法和Object.prototype.toString.call()方法進行對比。
var arr=[1,2];

//直接對一個陣列呼叫toString()
arr.toString();// "1,2"

//通過call指定arr陣列呼叫Object.prototype物件上原始的toString方法
Object.prototype.toString.call(arr); //"[object Array]"
複製程式碼

作為繼承的陣列arr重寫了toString方法,並不是Object.prototype中的toString方法。

  • 為什麼toString會有不同的作用呢? 其實,這裡面就涉及到js原型及原型鏈的相關知識
var arr=[1,2,3];
Object.prototype.toString.call(arr); //"[object Array]"
Array.prototype.toString.call(arr); // "1,2,3"
arr.toString(); // "1,2,3"
複製程式碼

看到這裡大家都應該明白了,其實只有Object.prototype上的toString才能用來進行復雜資料型別的判斷。

  • 簡單解釋一些原型鏈的概念:

我們都知道js中的物件都繼承自Object,所以當我們在某個物件上呼叫一個方法時,會先在該物件上進行查詢,如果沒找到則會進入物件的原型(也就是.prototype)進行查詢,如果沒找到,同樣的也會進入物件原型的原型進行查詢,直到找到或者進入原型鏈的頂端Object.prototype才會停止。

所以,當我們使用arr.toString()時,不能進行復雜資料型別的判斷,因為它呼叫的是Array.prototype.toString。雖然Array也繼承自Object,但jsArray.prototype上重寫了toString,而我們通過Object.prototype.toString.call(arr)實際上是通過原型鏈呼叫了Object.prototype.toString

  • 精確判斷物件的型別 JavaScript 中一切都是物件,任何都不例外,對所有值型別應用 Object.prototype.toString.call() 方法結果如下:
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

複製程式碼

相關文章