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#call
或Function#apply
的第一个参数时,它会被装箱以便在调用期间成为this
。 (在严格模式下,this
可以是原语。)除非被调用的函数保留对包装对象的引用,否则在调用完成时它会被丢弃。
拆箱当然是相反的:从装箱对象中获取原语。
规范中的语言称拳击“转换”:
来自§7.1.13:
ToObject 抽象操作 converts 参数为 Object 类型的值...
但是,它同时将 unboxing 称为“转换”和“强制”:
来自§7.1.1:
抽象操作 ToPrimitive 将其输入参数转换为非对象类型
来自§4.3.16:
布尔对象可以强制为布尔值。
来自§4.3.19:
一个字符串对象可以被强制转换成一个字符串值...
归根结底,重要的是我们了解何时会发生什么。我怀疑 convert 和 coerce 之间的强烈区别并不是作者有意做出的。
【讨论】:
【参考方案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 关键帧动画“强制”到其最终状态吗?
响应式设计损坏(仅在 iPhone 上)- 可以使用 javascript 强制执行媒体查询吗?