在You Don't Know JS - the Coercion Chapter我已经读到了强制,你永远不会得到一个复杂值的结果,比如对象或数组。拳击在准确的意义上不被视为强制。如何在引擎盖后面的JavaScript与强制不同?我真的看不出表面上的任何差异。
答案 0 :(得分:21)
这主要是语义问题。
首先,让我们定义“装箱”,因为这个术语在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:
布尔对象可以强制为布尔值。
来自§4.3.19:
String对象可以强制为String值...
在一天结束时,重要的是我们了解什么时候会发生什么。我怀疑作者没有故意做出 convert 和 coerce 之间的强烈区别。
答案 1 :(得分:1)
拳击和强制是不同的事物,可以独立发生,也可以彼此独立发生,或者两者同时发生。
如果看到装箱正在转换给定值的类型,那么转换* 和装箱都是这样。
例如:
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
处的强制是否与new String( 123 )
处的转换由JavaScript引擎的同一部分完成,但是我认为这样思考是有效的。 / p>
您可以使用拳击来做只能对对象执行的操作,例如:
var s = new String('a');
s.id = 123
// --> String { 0: "a", id: 123, length: 1 }
我从不需要显式地使用拳击,而只是隐式地例如在"abc".charAt(0)
。
无论如何,根据我的理解,胁迫一词用于强调一个事实,即隐式发生(在其他类型的上下文中),而不是强制转换或转换。那意味着永远不会有明确的强制!你真的不能做强制。强制只是发生。
但是可以使用强制规则来强制类型,例如:'' + 3
实际上是字符串连接,但是由于隐式强制,可以将其用于转换。另一方面,+'3'
或Number('3')
将是显式转换,而不是强制转换。 (规范似乎在这里没有明确区分。)
因此,以一种自以为是的方式重新表述以上内容:
答案 2 :(得分:0)
T.J。人群提供正确的答案。只是要补充一点,术语“装箱”在JavaScript世界中通常不使用,并且规范不使用它,但它确实存在。将原始对象包装为对象是拳击的一种形式,YDKJS的凯尔·辛普森在他的演讲和书籍中都非常清楚,拳击是一种隐含的强制形式。
不幸的是,这种误解是仍然经常遇到“一切都是JavaScript中的对象”这一古老(错误的)说法的部分原因。如果人们被告知原始不是对象,但是JS实体可以将它们强制为拳击形式,则这些误解将会消失。