1. 程式人生 > >【資料型別】JavaScript資料型別&聊聊Object.prototype.toString

【資料型別】JavaScript資料型別&聊聊Object.prototype.toString

一、資料型別

JavaScript定義了7種基本資料型別:

  • Symbol (ES6)
  • String
  • Number
  • Null
  • Boolean
  • Undefined
  • Object

JavaScript是一種弱型別指令碼語言,即定義變數時不需要宣告變數的型別,在程式運作過程中會自動判斷變數的型別,定義變數的方法:

  • var
  • let (ES6)
  • const (ES6)

不新增關鍵詞var/let/const也能定義變數,此時會建立一個全域性變數,且變數不會提升

二、值型別和引用型別

除了以上型別的劃分,變數還分為值型別和引用型別,在傳遞過程中分為值傳遞和引用傳遞。值傳遞只會傳遞變數的值,引用傳遞會傳遞變數的儲存地址(指標)

var a = 1,
	b = {name: 1};
	
var c = a; //值傳遞,a會賦值給c
console.log(c); // 1

c = 2;
console.log(a); // 1 值傳遞相當於記憶體中複製了一份,c和a沒有關係

var d = b;
d.name = 2;
console.log(b.name); // 2 引用傳遞傳遞的是地址,所以b和d指向同一記憶體地址

三、判斷資料型別的方法

我們很多時候需要對資料的型別進行判斷,然後進行相關的操作,判斷資料型別常見有以下幾種方法:

  • typeof
  • instanceof / constructor
  • Object.prototype.toString.call()

四、typeof

呼叫typeof方法時會返回以下幾種值之一:

  • symbol
  • string
  • number
  • boolean
  • undefined
  • object
  • function
typeof Symbol(); // symbol
typeof 'abc'; // string
typeof 123; // number
typeof NaN; // number
typeof true; // boolean
typeof undefined; // undefined
typeof {a:1}; // object
typeof [1,2,3]; // object
typeof function a(){}; // function
typeof null; // object // typeof檢測一個未宣告的變數時會返回undefined typeof undeclare_variable; // undefined
  • typeof null => object 這是typeof 的一個bug,null並不是引用型別
    Javascript語言第一版只設計了五種資料型別(物件、整數、浮點數、字串、布林值),沒考慮null,只把它當作object的一種特殊值,後來null獨立出來,作為一種單獨的資料型別,為了相容以前的程式碼就沒有改變null的型別
  • typeof [1,2,3] => object 除了函式返回的是function,其他引用型別返回的都是object,所以typeof無法區分array和object

五、instanceof / constructor

instanceof和constructor都是用於例項和建構函式的對應,可以彌補typeof無法區分陣列和物件的問題
但是,instanceof只能用於非物件型別資料的判斷

[1,2,3] instanceof Array; // true

var a = {name: 1};
a instanceof Object; // true

1 instanceof Number; // false

constructor可以用於任何型別的判斷

var num = 1;
num.constructor === Number; // true

var arr = [1, 2, 3];
arr.constructor === Array; // true

但是,這兩種方法都不能用於跨iframe的情況,例如

//pageOne中
...
<div>父頁面</div>
<iframe src="pageB.html" frameborder="0"></iframe>
<script>
  //這裡定義了一個數組a
  var a = [1, 2, 3];
  console.log( a.constructor === Array ); // true
</script>
...

//pageTwo中
...
<div>子頁面</div>
<script>
  //子頁面獲取父頁面中的值
  var c = top.a;
  console.log( c.constructor === Array ); // false
</script>
...

這是因為瀏覽器會把不同頁面中的Array做區分,即pageOne和pageTwo的Array是不同的兩個構造器,所以跨iframe的變數無法通過這兩種方法來判斷

六、Object.prototype.toString.call()

使用typeof無法區分null、array、date
instanceof無法運用在值型別區分
沒事,我們還有一種方法,Object.prototype.toString.call()

Object.prototype.toString.call()是一種比較常用,也是比較有效的方法

var a = {name: 1};
Object.prototype.toString.call(a); // [object Object]

var b = new Date();
Object.prototype.toString.call(b); // [object Date]

Object.prototype.toString.call(null); // [object Null]

Object.prototype.toString.call(undefined); // [object Undefined]

function a(){
	console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
}

通過這種方式我們就可以解決常見問題中的型別判斷

那麼,為什麼Object.prototype.toString.call()可以區分資料型別?

七、探索為什麼Object.prototype.toString.call()可以區分資料型別

我們先來了解一下在呼叫Object.prototype.toString.call()的時候發生了什麼

1.在ECMA5中,呼叫Object.prototype.toString會執行以下步驟:

這裡寫圖片描述

(1).如果值為undefined,則返回"[object Undefiend]"

(2).如果值為null,則返回"[object Null]"

(3).將傳遞的值呼叫ToObject方法,並賦給O作為呼叫後的結果

(4).將O的內部屬性[[Class]]的值賦給class

(5).結果返回字串,該字串由三個字串"[object ", class, and "]"拼接而成

根據上面的描述我們可以看出來,主要通過物件的內建屬性[[Class]]來判斷

2.內建屬性[[Class]]

ECMA規範中這麼介紹[[Class]]
這裡寫圖片描述
這裡寫圖片描述

翻譯:本規範針對每種內建物件定義了[[Class]]內部屬性的值。物件的[[Class]]內部屬性的值可以是除了。。。之外的任何字串值。[[Class]]內部屬性的值用於內部區分不同型別的物件。 請注意,除了通過Object.prototype.toString外,本規範沒有提供任何方法讓程式訪問該值。

我們再來看規範中,在建立某種型別變數的時候,會對物件內建屬性[[Class]]進行賦值

  • 建立陣列時:
    這裡寫圖片描述

  • 建立函式時:
    這裡寫圖片描述

  • 建立字串時:
    這裡寫圖片描述

3.ES6中不再使用[[Class]]內建屬性進行判斷

最新規範中不再使用[[Class]]內建屬性進行判斷,在呼叫Object.prototype.toString時執行了以下步驟:
這裡寫圖片描述

(這裡不詳細講,有興趣的可以去了解下)

綜上,大家應該瞭解了為什麼Object.prototype.toString.call()可以用來區分資料型別

一些問題

1.null和undefined不是物件,為什麼Object.prototype.toString.call()返回的值要採用[object *]的格式

這是ES5.1規範中的修正。 在ES5之前,將null或undefined傳遞給toString總是導致全域性物件被傳遞。 ES5中嚴格模式相關的更改會導致null和undefined被傳遞而不進行修改。 按照ES5的規定,將null或undefined傳遞給Object.prototype.toString會導致TypeError異常。 這個例外破壞了一些現有的程式碼,所以ES5.1修復這個規範,以防止這種情況發生。

那麼,為什麼toString返回null和undefined? 事實證明,很多現有的程式碼也期望Object.prototype.toString始終返回形式為“[object *]”的字串。 因此,ES5.1規範中決定將傳入null和undefined的值“[object Null]”和“[object Undefined]”。

2.Object.prototype.toString(‘foo’)和Object.prototype.toString.call(‘foo’)的區別

前者傳入的引數實際是Object.prototype,而Object.prototype是個物件,所以始終返回’[object Object]’,其中的’foo’會被忽略;而後者通過call,硬繫結到傳入的’foo’;所以千萬不要把call忘了