在《你不知道的JavaScript - 强制类型转换章节》中,我读到了这样一段话:使用强制类型转换,你永远得不到一个复杂值的结果,例如对象或数组。但是,盒装对象并没有被准确地认为是强制类型转换。那么,盒装对象在 JavaScript 中与强制类型转换有什么不同呢?从表面上看,我真的看不出任何区别。
在《你不知道的JavaScript - 强制类型转换章节》中,我读到了这样一段话:使用强制类型转换,你永远得不到一个复杂值的结果,例如对象或数组。但是,盒装对象并没有被准确地认为是强制类型转换。那么,盒装对象在 JavaScript 中与强制类型转换有什么不同呢?从表面上看,我真的看不出任何区别。
这主要是一个语义学问题。
首先,让我们定义一下“boxing”,因为这个术语在JavaScript中并不常用(例如,在规范中没有出现):
“Boxing”是将一个原始值包装在对象中。例如,new Number(42)
会为原始数字42创建一个Number
对象。
JavaScript中唯一的自动拆箱是:
当你在一个原始值上使用方法时,像这样:
console.log("testing".toUpperCase());
"testing"
是一个基本字符串,因此没有方法。当JavaScript引擎看到具有原始根的属性访问器操作时,根据规范它会为该原始值创建一个包装对象(例如,基本字符串的String
对象)然后检索属性。如果正在调用属性(例如"foo".toUpperCase()
),在松散模式下包装对象是调用内部的this
(在严格模式下是原始字符串)。除非在方法调用中保留了包装对象,否则之后就被丢弃。
当您在松散模式下使用基元作为Function #call
或Function #apply
的第一个参数时,它会被包装以便在调用期间成为this
。(在严格模式下,this
可能是一个基元)。除非被调用的函数保留了对包装对象的引用,否则在调用完成后它将被丢弃。
当然,“拆箱”是相反的过程:从包装对象中获取基元。
规范中的语言称包装为“转换”:
来自§7.1.13:
抽象操作ToObject将参数转换为类型为Object的值...
但是,它将“拆箱”称为“转换”和“强制转换”:
来自§7.1.1:
抽象操作ToPrimitive将其输入参数转换为非对象类型
来自§4.3.16:
可以将Boolean对象强制转换为布尔值。
来自§4.3.19:
可以将字符串对象强制转换为字符串值...
归根结底,重要的是我们理解何时发生了什么。我怀疑作者故意没有对转换和强制转换之间做出严格区分。
装箱和强制类型转换是不同的事情,它们可以独立发生,一个或两者同时发生。
如果您看到装箱正在转换给定值的类型,那么这既是转换*又是装箱。
例如:
var sp = 'abc'; // string primitive
// boxing
var so = new String( sp ); // string object, created from string primitive
// first conversion* and then boxing
var sn = new String( 123 ); // string object, created from a number
// coercion without boxing
var n = '3' - 1; // number 2
*) 我不知道在'3' - 1
处的强制转换是否由javascript引擎的同一部分执行,但我认为这样思考是有效的。
您可以使用装箱来执行仅适用于对象的操作,例如:
var s = new String('a');
s.id = 123
// --> String { 0: "a", id: 123, length: 1 }
"abc".charAt(0)
。'' + 3
实际上是字符串连接,但由于隐式强制转换,它可用于转换。另一方面,+'3'
或 Number('3')
将是显式转换,而不是强制转换。(规范似乎在这里没有明确区分。)