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 可用呢 ?
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。
typeof
let str1 = 'hello'
let str2 = new String('hello')
typeof str1 // ?
typeof str2 // ?
雖然都是 hello
,但由 primitive wrapper 屬於 object。
Equaivalence
let str1 = 'hello'
let str2 = new String('hello')
str1 === str2 // ?
str1 === str2.valueOf() // ?
str1 == str2 // ?
若使用 ===
比較,由於 str1
與 str2
型別已經不同,故回傳 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
得到更精確判斷。
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。
Conclusion
- String、number 與 boolean 雖然是 primitive,但依然提供 constructor function,使其能動態產生 primitive wrapper object,使用完後再動態銷毀
undefined
與null
雖然也是 primitive,但沒有 primitive wrapper 可用- Primitive wrapper 本質是 object,而非 primitive,可靠
valueOf()
取出內部值 - 實務上甚少直接使用 primitive wrapper,僅讓其背後動態產生與銷毀,但若需要使用 dynamic property 時,可視情況使用
Reference
許國政, 008 天重新認識 JavaScript