原始类型的值,可以自动当作对象调用,即调用各种对象的方法和参数。这时,JavaScript引擎会自动将原始类型的值转为包装对象,在使用后立刻销毁。
例如,字符串可以调用length属性,返回字符串的长度。
'abc'.length // 3
在上面代码中,abc是一个字符串,本身不是对象,不能调用length属性。JavaScript引擎自动将其转为包装对象,在这个对象上调用length属性。调用结束后,这个临时对象就会被销毁。这就叫原始类型的自动转换。例如:
var str = 'abc'; str.length // 3 // 等同于 var strObj = new String(str) // String { // 0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc" // } strObj.length // 3
在上面代码中,字符串abc的包装对象有每个位置的值、有length属性、还有一个内部属性[[PrimitiveValue]]保存字符串的原始值。这个[[PrimitiveValue]]内部属性,外部是无法调用,仅供valueOf或toString这样的方法内部调用。
这个临时对象是只读的,无法修改。所以,字符串无法添加新属性。例如:
var s = 'Hello World'; s.x = 123; s.x // undefined
上面代码为字符串s添加了一个x属性,结果无效,总是返回undefined。
另一方面,调用结束后,临时对象会自动销毁。这意味着,下一次调用字符串的属性时,实际是调用一个新生成的对象,而不是上一次调用时生成的那个对象,所以取不到赋值在上一个对象的属性。如果想要为字符串添加属性,只有在它的原型对象String.prototype上定义。
这种原始类型值可以直接调用的方法还有很多,除了前面介绍过的valueOf和toString方法,还包括三个包装对象各自定义在原型上的方法。例如:
'abc'.charAt === String.prototype.charAt // true
上面代码表示,字符串abc的charAt方法,实际上就是定义在String对象原型上的方法。
如果包装对象与原始类型值进行混合运算,包装对象会转化为原始类型。例如:
new Number(123) + 123 // 246 new String('abc') + 'abc' // "abcabc"