JavaScript中的boxing是强制类型转换吗?

17

《你不知道的JavaScript - 强制类型转换章节》中,我读到了这样一段话:使用强制类型转换,你永远得不到一个复杂值的结果,例如对象或数组。但是,盒装对象并没有被准确地认为是强制类型转换。那么,盒装对象在 JavaScript 中与强制类型转换有什么不同呢?从表面上看,我真的看不出任何区别。


1
“像对象函数一样”是什么意思?“对象函数”是什么? - T.J. Crowder
可能是自动装箱和强制转换有什么区别?的重复问题。 - Krystian Laskowski
1
@KrystianLaskowski:这是一个Java问题。这本书和问题标签都是关于JavaScript的。 - T.J. Crowder
@T.J.Crowder YDKJ的这一章讨论了装箱和拆箱,好像它确实存在于JavaScript中。 - sdgluck
@T.J.Crowder 对,我也是这么想的。抱歉,我的评论失去了上下文,因为它是作为回应你在一个现已删除的评论中宣称相反(即 JavaScript 中不存在装箱)而发表的。但我现在明白了,你是将其作为术语而非语言特性来讨论的。 - sdgluck
显示剩余4条评论
3个回答

34

这主要是一个语义学问题。

首先,让我们定义一下“boxing”,因为这个术语在JavaScript中并不常用(例如,在规范中没有出现):

“Boxing”是将一个原始值包装在对象中。例如,new Number(42)会为原始数字42创建一个Number对象。

JavaScript中唯一的自动拆箱是:

  1. 当你在一个原始值上使用方法时,像这样:

    console.log("testing".toUpperCase());
    

    "testing"是一个基本字符串,因此没有方法。当JavaScript引擎看到具有原始根的属性访问器操作时,根据规范它会为该原始值创建一个包装对象(例如,基本字符串的String对象)然后检索属性。如果正在调用属性(例如"foo".toUpperCase()),在松散模式下包装对象是调用内部的this(在严格模式下是原始字符串)。除非在方法调用中保留了包装对象,否则之后就被丢弃。

    当您在松散模式下使用基元作为Function #callFunction #apply的第一个参数时,它会被包装以便在调用期间成为this。(在严格模式下,this可能是一个基元)。除非被调用的函数保留了对包装对象的引用,否则在调用完成后它将被丢弃。

    当然,“拆箱”是相反的过程:从包装对象中获取基元。

    规范中的语言称包装为“转换”:

    来自§7.1.13

    抽象操作ToObject将参数转换为类型为Object的值...

    但是,它将“拆箱”称为“转换”和“强制转换”:

    来自§7.1.1

    抽象操作ToPrimitive将其输入参数转换为非对象类型

    来自§4.3.16

    可以将Boolean对象强制转换为布尔值。

    来自§4.3.19

    可以将字符串对象强制转换为字符串值...

    归根结底,重要的是我们理解何时发生了什么。我怀疑作者故意没有对转换强制转换之间做出严格区分。


2

装箱强制类型转换是不同的事情,它们可以独立发生,一个或两者同时发生。

  • 装箱是将原始类型包装在对象中。
  • 强制类型转换就像是将原始类型解释为不同的类型。

如果您看到装箱正在转换给定值的类型,那么这既是转换*又是装箱。

例如:

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') 将是显式转换,而不是强制转换。(规范似乎在这里没有明确区分。)
因此,为了以自己的观点重新表述上述观点:
- 装箱是将基元包装在对象内。 - 强制转换是会发生的事情,而不是你可以做的事情。 - 可以通过显式运用、装箱或强制转换进行转换。

1
T.J. Crowder给出了正确的答案。值得补充的是,“boxing”这个术语在JavaScript世界中并不常用,规范也没有使用它,但它确实存在。将基本类型包装成对象是一种装箱形式,YDKJS的Kyle Simpson在他的演讲和书籍中非常清楚地表明,装箱是一种隐式强制转换形式。不幸的是,这种误解正是“JavaScript中一切皆为对象”的旧说法(错误的说法)仍然经常遇到的原因之一。如果人们被教导基本类型不是对象,但可以通过JS引擎以装箱的形式强制转换为对象,很多这些误解就会消失。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接