第三章 內建物件

王工發表於2016-04-29

前面兩章曾多次提到過JS的內建物件(natives),像是String 或是 Number 這類。現在我們要深入的瞭解它們了,嘿嘿嘿。

下面列出JS中常見的內建物件

  1. String()
  2. Boolean()
  3. Number()
  4. Object()
  5. Function()
  6. Array()
  7. Date()
  8. Error()
  9. RegExp()
  10. Symbol() // ES6

如果在瀏覽器中,還有各類的 HTMLDomElement ,ES6裡也有許多新的內建物件。

你看,這些內建物件怎麼像函式呢?其實你可以把它們理解成物件建構函式。

var s  = new String('I am a String');
typeof s; //=> 'object'

我們使用之前用過的typeof 檢查一下,並沒有什麼區別,可以認為內建物件是物件的子型別,typeof只能顯式值的型別,因此只能顯示 'object' 'function' 和 'symbol';

[[Class]]屬性

既然 typeof 無能為力,那如何確定其他的內建物件的型別呢。這些內建物件都有一個內建的隱藏屬性[[Class]],需要用 Object.prototype.toString() 使用 call 繫結到該內建物件上來獲取。

  Object.prototype.toString.call( [ ] ); //=> "[object Array]"

返回值中的 Array 就是[[Class]]的屬性了,但是這個方法一樣有些奇怪的地方

  Object.prototype.toString.call( undefined ); //=> "[object Undefined]"
  Object.prototype.toString.call( null ); //=> "[object Null ]"
  Object.prototype.toString.call( 1 ); //=> "[object Number]"
  Object.prototype.toString.call( 'build-in type' ); //=> "[object String]"

WTF...and more. JS 這個語言中檢測方法怎麼都不按照基本法啊?!內建物件並沒有 null 和 undefined 啊,你們到底湊什麼熱鬧啊,還有 1 和 'buid...' 明明是內建型別,怎麼也成 object 了 ??那是因為內建型別會被裝箱(Boxing Wrappers),那什麼是裝箱呢,裝箱的東西是能吃還是能走私呢?

裝箱(Boxing Wrappers)

前面我們看到在檢查內建型別的時候,它們變成了物件,這種行為就叫裝箱。那裝箱有什麼用呢?我猜你可能使用過這個功能但全然沒注意過這個問題

'abc'.length; // 3
'lower'.toUpperCase(); // 'LOWER'

看到了吧,第二本書就介紹了JS的原型鏈機制,我們要知道JS中的所有內建方法都儲存在某個建構函式的prototype物件中,但是像 'abc' 1 這種內建型別並不是物件,沒有屬性也沒有方法,自然也沒有[[proto]](__proto__),不能依靠原型鏈向上呼叫方法,因此在執行某些方法時,JS會把內建型別裝箱成物件,讓其獲取呼叫原型鏈上的方法的能力。

看到這裡你一定想到了一個好主意,就是如果你須要在一個 for 迴圈中 使用 'abc'.length,那JS不是每次都要進行裝箱,你可以預先構造一個 new String('abc') 物件,這樣是不是可以加速執行程式碼了?有趣的問題,你可千萬不要這麼做,因為這個問題早期的開發者早就想到了,因此他們已經做了優化,而如果你想來個預優化處理很可能適得其反。所以建議你不要使用建構函式建立一個內建型別對應的內建物件,而是讓JS自己去裝箱。

拆箱(Unboxing)

既然能裝箱,必然也有拆箱的方法,那就是呼叫 valueOf( ) 這個函式,可以將一個內建物件的primitive值取出。

valueOf( new String('abc') ); // 'abc'

這是一種顯式的拆箱方法,而隱式的拆箱方法在第四章 強制轉型中介紹。

本章小結

  1. JS的內建物件有10種,都是由 JS 的建構函式用 new 操作符構造的物件(symbol不用new)
  2. 內建物件有個[[Class]]隱藏屬性,需要用 Object.prototype.toString.call( native )獲取
  3. 上一條的方法也可以對內建型別獲取其 [[Class]] 屬性,原因是 JS 會在需要時對內建型別進行裝箱
  4. 裝箱可以讓內建型別獲取原型鏈函式呼叫,但不要手動構造內建型別的物件,這樣並不會提升效能
  5. 對內建物件應用 valueOf() 方法可以獲取內建物件的 值,這個過程叫拆箱,另外的拆箱方法是強制轉型,第四章介紹。

相關文章