JavaScript中的拳击强制吗?

Posted

技术标签:

【中文标题】JavaScript中的拳击强制吗?【英文标题】:Is boxing coercion in JavaScript? 【发布时间】:2016-03-08 03:23:59 【问题描述】:

在You Don't Know JS - the Coercion Chapter 中,我读过强制转换,您永远不会得到复杂值的结果,例如对象或数组。拳击并不被认为是准确意义上的强制。装箱与 javascript 中的强制转换有何不同?表面上我真的看不出有什么不同。

【问题讨论】:

"...类对象函数" 什么是“对象函数”? What is the difference between autoboxing and coercion?的可能重复 @KrystianLaskowski:这是一个 Java 问题。这本书和问题上的标签都是关于 JavaScript. @T.J.Crowder This chapter of YDKJ 谈论装箱和拆箱,就好像它确实存在于 JavaScript 中一样。 @sdgluck:确实如此 (new Number(42)),这不是 JavaScript 通常使用的术语。 【参考方案1】:

这主要是语义问题。

首先,让我们定义“装箱”,因为这个词在 JavaScript 中并不常用(例如,它没有出现在规范中):

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

在 JavaScript 中完成的唯一自动装箱是:

    当你在一个基元上使用一个方法时,像这样:

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

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

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

拆箱当然是相反的:从装箱对象中获取原语。

规范中的语言称拳击“转换”:

来自§7.1.13:

ToObject 抽象操作 converts 参数为 Object 类型的值...

但是,它同时将 unboxing 称为“转换”和“强制”:

来自§7.1.1:

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

来自§4.3.16:

布尔对象可以强制为布尔值。

来自§4.3.19:

一个字符串对象可以被强制转换成一个字符串值...

归根结底,重要的是我们了解何时会发生什么。我怀疑 convertcoerce 之间的强烈区别并不是作者有意做出的。

【讨论】:

【参考方案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 的强制转换是否与new String( 123 ) 的转换是由javascript 引擎的同一部分完成的,但我认为这样想是有效的。

你可以使用拳击来做你只能用对象做的事情,例如:

var s = new String('a');
s.id = 123
// --> String  0: "a", id: 123, length: 1 

我从不需要显式使用拳击,只需要隐式使用,例如"abc".charAt(0).

(意见:)

无论如何,在我的理解中,coercion 这个词是用来强调它隐式发生的事实(在其他类型的上下文中),而不是单词转换或转换。这意味着永远不会有明确的强制!你不能真正强制。强制只是发生

但是可以利用强制的规则来强制一个类型,例如:'' + 3 实际上是一个字符串连接,但是由于隐式强制它可以用于转换。另一方面,+'3'Number('3') 将是显式转换,而不是强制转换。 (规范似乎在这里没有明确区分。)

因此,以一种自以为是的方式重新表述上述内容:

装箱是将一个基元包装在一个对象中。 强制是发生的事情,而不是您可以做的事情。 转换可以显式完成,也可以通过使用拳击或强制转换来完成

【讨论】:

【参考方案3】:

T.J.克劳德给出了正确的答案。顺便说一句,“装箱”一词在 JavaScript 世界中通常不使用,规范也没有使用它,但它确实存在。将原语包装为 Objects 是一种拳击形式,YDKJS 的 Kyle Simpson 在他的演讲和书籍中非常清楚,拳击是一种隐式强制形式。

不幸的是,这种误解是“JavaScript 中的一切都是对象”的陈旧(且不正确)声明仍然经常遇到的部分原因。如果人们被教导原语不是对象,但是可以通过 JS entine 以拳击的形式强制他们充当对象,那么很多这些误解就会消失。

【讨论】:

以上是关于JavaScript中的拳击强制吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以使用 javascript 强制浏览器“刷新”任何待处理的布局更改吗?

您可以使用 JavaScript 将 CSS 关键帧动画“强制”到其最终状态吗?

如何在Scala中进行拳击?

响应式设计损坏(仅在 iPhone 上)- 可以使用 javascript 强制执行媒体查询吗?

在Chrome扩展程序中,您可以强制在一切之前注入一些javascript吗?

JavaScript:字段或属性