點燈坊

失くすものさえない今が強くなるチャンスよ

ECMAScript 之 Primitive Wrapper

Sam Xiao's Avatar 2020-01-11

String 雖然為 Primitive,但為什麼卻也能如 Object 有 lengh property 呢 ? 這一切的黑魔法都來自於 Primitive Wrapper。

Version

macOS Catalina 10.15.2
VS Code 1.41.1
Quokka 1.0.271
ECMAScript 2015

Primitive

let str = 'hello'
str.length // ?

String 明明是 primitive,而非 object,為什麼有 length property 可用呢 ?

wrapper000

Primitive Wrapper

let str = 'hello'
let str = new String('hello')
str.length // ?

str = null
str = 'hello'

當存取 str.length 時,事實上 ECMAScript 會動態使用 new 搭配 String() constructor function 重新建立 str object,使其能讀取 str.length,用完之後馬上 str = null 銷毀 object,最後恢復成 primitive。

wrapper001

typeof

let str1 = 'hello'
let str2 = new String('hello')

typeof str1 // ?
typeof str2 // ?

雖然都是 hello,但由 primitive wrapper 屬於 object。

wrapper002

Equaivalence

let str1 = 'hello'
let str2 = new String('hello')

str1 === str2 // ?
str1 === str2.valueOf() // ?

str1 == str2 // ?

若使用 === 比較,由於 str1str2 型別已經不同,故回傳 false

但若使用 valueOf() 取出 primitive wrapper 內部值時,由於 === 左右都是 string,故回傳 true

若使用 ==,primitive wrapper 會自動轉型成 primitive,故回傳 true

instanceOf

let str = new String('hello')
let num = new Number(3)

str instanceof String // ?
num instanceof Number // ?

也由於 primitive wrapper 由 constructor function 建立,若嫌 typeof 都回傳 object 太粗糙,可由 instanceOf 得到更精確判斷。

wrapper003

Dynamic Property

let str1 = 'hello'
let str2 = new String('hello')

str1.name = 'world'
str2.name = 'world'

let fn = ({ name }) => `${str1} ${name}`

fn(str1) // ?
fn(str2) // ?

ECMAScript 為動態語言,可直接對 object 動態新增 property,但 primitive 則不行。

若要對 string 動態新增 property,則要使用 primitive wrapper。

wrapper004

Conclusion

  • String、number 與 boolean 雖然是 primitive,但依然提供 constructor function,使其能動態產生 primitive wrapper object,使用完後再動態銷毀
  • undefinednull 雖然也是 primitive,但沒有 primitive wrapper 可用
  • Primitive wrapper 本質是 object,而非 primitive,可靠 valueOf() 取出內部值
  • 實務上甚少直接使用 primitive wrapper,僅讓其背後動態產生與銷毀,但若需要使用 dynamic property 時,可視情況使用

Reference

許國政, 008 天重新認識 JavaScript