Nat*_*ong 247 javascript
最近的一条推文包含了这段JavaScript代码.
有人可以一步一步解释其中发生的事情吗?
> function dis() { return this }
undefined
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
> five * 5
25
> five.wtf
"potato"
> five++
5
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
> five
6
Run Code Online (Sandbox Code Playgroud)
特别是,我不清楚:
dis.call(5)是Number带有某种[[PrimitiveValue]]属性的,但结果five++和five * 5看起来只是普通的数字5和25(不是Numbers)five.wtf属性在five++增量后消失five.wtf属性在five++增量后不再可five.wtf = 'potato?'设置,尽管分配显然设置了值.Mat*_*hew 278
OP在这里.有趣的是在Stack Overflow上看到这个:)
在逐步完成这一行为之前,澄清一些事情很重要:
数值和数字对象(a = 3vs a = new Number(3))是非常不同的.一个是原始的,另一个是对象.您不能将属性分配给基元,但可以分配给对象.
两者之间的强制是隐含的.
例如:
(new Number(3) === 3) // returns false
(new Number(3) == 3) // returns true, as the '==' operator coerces
(+new Number(3) === 3) // returns true, as the '+' operator coerces
Run Code Online (Sandbox Code Playgroud)每个Expression都有一个返回值.当REPL读取并执行表达式时,这就是它显示的内容.返回值通常并不意味着你的想法,而是暗示那些不正确的事情.
好的,我们走了.

> function dis() { return this }
undefined
> five = dis.call(5)
[Number: 5]
Run Code Online (Sandbox Code Playgroud)
定义一个函数dis并调用它5.这将使用5context(this)执行该函数.这里它是从Number值强制转换为Number对象.值得注意的是,如果我们处于严格模式, 那就不会发生这种情况.
> five.wtf = 'potato'
'potato'
> five.wtf
'potato'
Run Code Online (Sandbox Code Playgroud)
现在我们将属性设置five.wtf为'potato',并将五个作为对象,确定它接受简单分配.
> five * 5
25
> five.wtf
'potato'
Run Code Online (Sandbox Code Playgroud)
随着five作为对象,我保证它仍然可以进行简单的算术运算.它可以.它的属性还坚持吗?是.
> five++
5
> five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
现在我们检查five++.后缀增量的技巧是整个表达式将根据原始值进行评估,然后递增值.它看起来five仍然是五,但实际上表达式评估为五,然后设置five为6.
不仅five设置为6,而且它被强制转换为Number值,并且所有属性都丢失了.由于原语不能保存属性,five.wtf因此未定义.
> five.wtf = 'potato?'
'potato?'
> five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
我再次尝试将属性重新分配wtf给five.返回值意味着它坚持,但它实际上并不是因为five是Number值,而不是Number对象.表达式求值为'potato?',但是当我们检查时,我们看到它没有被分配.
> five
6
Run Code Online (Sandbox Code Playgroud)
自从postfix增量以来,five一直如此6.
dec*_*eze 78
有两种不同的方式来表示数字:
var a = 5;
var b = new Number(5);
Run Code Online (Sandbox Code Playgroud)
第一个是基元,第二个是对象.对于所有意图和目的,两者都表现相同,除非它们在打印到控制台时看起来不同.一个重要的区别是,作为一个对象,new Number(5)接受新属性就像任何普通的{},而原始的5不是:
a.foo = 'bar'; // doesn't stick
b.foo = 'bar'; // sticks
Run Code Online (Sandbox Code Playgroud)
至于初始dis.call(5)部分,请参阅"this"关键字如何工作?.让我们说第一个参数将call被用作值的值this,并且此操作会将数字强制为更复杂的Number对象形式.*稍后会++强制它返回到基本形式,因为加法运算会+产生一个新的基元.
> five = dis.call(5) // for all intents and purposes same as new Number(5)
Number {[[PrimitiveValue]]: 5}
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
Run Code Online (Sandbox Code Playgroud)
甲Number对象接受新的属性.
> five++
Run Code Online (Sandbox Code Playgroud)
++产生一个新的原始6价值......
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
...没有和不接受自定义属性.
*请注意,在严格模式下,this参数将被区别对待,并且不会转换为a Number.有关实现的详细信息,请参见http://es5.github.io/#x10.4.3.
Lui*_*rez 59
JavaScript世界中存在强制 - 一个侦探故事
内森,你不知道你发现了什么.
我一直在调查这几周了.这一切都始于去年十月的暴风雨之夜.我不小心偶然发现了这个Number课程 - 我的意思是,为什么世界上有JavaScript有Number课?
我没准备好接下来要发现的东西.
事实证明,JavaScript在没有告诉你的情况下,一直在将你的数字更改为对象,将对象更改为数字.
JavaScript希望没有人会流行,但人们一直在报告奇怪的意外行为,现在感谢你和你的问题,我有证据表明我需要把这个事情吹得一团糟.
这是我们到目前为止所发现的.我不知道我是否应该告诉你 - 你可能想要关闭你的JavaScript.
> function dis() { return this }
undefined
Run Code Online (Sandbox Code Playgroud)
当您创建该功能时,您可能不知道接下来会发生什么.一切都很好看,一切都很好 - 现在.
没有错误消息,只是控制台输出中的"未定义"一词,正是您所期望的.毕竟,这是一个函数声明 - 它不应该返回任何东西.
但这只是一个开始.接下来发生了什么,没有人能够预测到.
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
Run Code Online (Sandbox Code Playgroud)
是的,我知道,你期待一个5,但那不是你得到的,是它 - 你得到别的东西 - 不同的东西.
这样的事情我也经历过.
我不知道该怎么做.它让我疯了.我无法入睡,我无法进食,我试着把它喝掉,但没有多少山露会让我忘记.它没有任何意义!
就在那时我发现了真正发生的事情 - 它是强制性的,它发生在我眼前,但是我太盲目无法看到它.
Mozilla试图通过将它放在他们不知道的人的位置来掩埋它 - 他们的文档.
经过几个小时的递归阅读和重新阅读并重新阅读后,我发现了这一点:
"......原始值将转换为对象."
它是正确的,因为可以用Open Sans字体拼写出来.这是call()功能 - 我怎么会这么愚蠢?!
我的号码不再是一个数字.我传递它的那一刻call(),它变成了别的东西.它变成了......一个对象.
起初我简直不敢相信.这怎么可能是真的?但我不能忽视我周围的证据.如果你只是看,它就在那里:
> five.wtf = 'potato'
"potato"
> five.wtf
"potato"
Run Code Online (Sandbox Code Playgroud)
wtf是正确的.数字不能有自定义属性 - 我们都知道!这是他们在学院教你的第一件事.
我们应该知道我们看到控制台输出的那一刻 - 这不是我们认为的数字.这是一个冒名顶替者 - 一个将自己作为我们甜蜜的无辜号码的对象.
这是... new Number(5).
当然!它非常有意义.call()我有一份工作要做,他必须调用一个功能,为了做到这一点,他需要填充this,他知道他不能用一个数字来做 - 他需要一个物体,他愿意做任何事来得到它,甚至如果这意味着强迫我们的号码.当call()看到这个号码时5,他看到了一个机会.
这是一个完美的计划:等到没有人在寻找并换出我们的数字来寻找一个看起来像它的物体.我们得到一个数字,函数被调用,没有人会更聪明.
这真是一个完美的计划,但是就像所有计划一样,即使是完美的计划,也有一个洞,我们即将陷入其中.
看,有什么call()不明白的是,他不是镇上唯一能够强迫数字的人.毕竟这是JavaScript - 强制无处不在.
call() 拿走了我的电话号码,直到我把面具从他的小冒名顶替者身上移开并将他暴露给整个Stack Overflow社区之后我才会停下来.
但是怎么样?我需要一个计划.当然它看起来像一个数字,但我知道它不是,有必要证明这一点.而已!它看起来像一个数字,但它可以像一个?
我告诉five我需要他变大5倍 - 他没有问为什么,我没有解释.然后,我做了任何优秀的程序员会做的事情:我成倍增加.当然,他无法伪造自己的方式.
> five * 5
25
> five.wtf
'potato'
Run Code Online (Sandbox Code Playgroud)
该死的!不仅five繁殖好了wtf仍然存在.该死的这个家伙和他的土豆.
到底是怎么回事?这整件事我错了吗?是five真的多少?不,我必须遗漏一些东西,我知道,有一些我必须忘记的东西,一些如此简单和基本的东西,我完全忽视它.
这看起来不太好,我几个小时都在写这个答案,我仍然没有接近我的观点.我无法保持这种状态,最终人们会停止阅读,我不得不想一些事情,我不得不快速地想到它.
等等吧!five不是25,25是结果,25是完全不同的数字.当然,我怎么能忘记?数字是不可变的.当你乘以5 * 5任何东西时,你只需要创建一个新数字25.
那一定是这里发生的事情.不知何故,当我乘以时five * 5,five必须被强制转换为数字,并且该数字必须是用于乘法的数字.这是乘法的结果,打印到控制台,而不是five自身的值.five永远不会被分配任何东西 - 所以当然它不会改变.
那么我该如何five为自己分配一个操作的结果.我知道了.在five有机会思考之前,我大喊"++".
> five++
5
Run Code Online (Sandbox Code Playgroud)
啊哈!我有他!大家都知道5 + 1就是6,这是我需要公开的证据five是不是一个数字!这是冒名顶替者!一个不知道怎么算的坏冒名顶替者.我可以证明这一点.以下是实数的作用:
> num = 5
5
> num++
5
Run Code Online (Sandbox Code Playgroud)
等待?这里发生了什么?叹了口气,我陷入了破坏five,我忘记了后期操作员的工作方式.当我++在最后使用时,five我说要返回当前值,然后递增five.这是操作发生之前打印到控制台的值.num事实上6,我可以证明这一点:
>num
6
Run Code Online (Sandbox Code Playgroud)
是时候看看到底five是什么了:
>five
6
Run Code Online (Sandbox Code Playgroud)
......它应该是它应该是什么.five很好 - 但我更好.如果five仍然是一个对象意味着它仍然拥有财产wtf,我愿意打赌它没有的一切.
> five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
啊哈!我是正确的.我有他!five现在是一个数字 - 它不再是一个对象了.我知道乘法技巧这次不能保存它.看到five++的确如此five = five + 1.与乘法不同,运算++符为其赋值five.更具体地说,它为其赋予结果,five + 1就像在乘法的情况下返回一个新的不可变数字一样.
我知道我有他,只是为了确保他不能摆脱它.我还有一个测试我的袖子.如果我是对的,five现在真的是一个数字,那么这将不起作用:
> five.wtf = 'potato?'
'potato?'
Run Code Online (Sandbox Code Playgroud)
这次他不会欺骗我.我知道potato?将打印到控制台,因为这是作业的输出.真正的问题是,还会wtf存在吗?
> five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
正如我怀疑 - 没有 - 因为数字不能分配属性.我们了解到学院的第一年;)
谢谢内森.由于你有勇气提出这个问题,我终于可以把所有这些都放在我身后,继续研究一个新案例.
像这个关于功能的一个toValue().哦,亲爱的上帝.拿去!
zzz*_*Bov 28
01 > function dis() { return this }
02 undefined
03 > five = dis.call(5)
04 Number {[[PrimitiveValue]]: 5}
05 > five.wtf = 'potato'
06 "potato"
07 > five.wtf
08 "potato"
09 > five * 5
10 25
11 > five.wtf
12 "potato"
13 > five++
14 5
15 > five.wtf
16 undefined
17 > five.wtf = 'potato?'
18 "potato?"
19 > five.wtf
20 undefined
21 > five
22 6
Run Code Online (Sandbox Code Playgroud)
01声明一个dis返回上下文对象的函数.什么this代表更改取决于您是否使用严格模式.如果函数声明为:整个示例有不同的结果:
> function dis() { "use strict"; return this }
Run Code Online (Sandbox Code Playgroud)
- 如果函数代码是严格代码,请将ThisBinding设置为thisArg.
- 否则,如果thisArg为null或未定义,则将ThisBinding设置为全局对象.
- 否则,如果Type(thisArg)不是Object,则将ThisBinding设置为ToObject(thisArg).
02是函数声明的返回值.undefined应该在这里自我解释.
03使用在原始值的上下文中调用时five的返回值初始化变量.因为不是严格模式,所以此行与调用相同.dis5disfive = Object(5)
04奇数Number {[[PrimitiveValue]]: 5}返回值是包装原始值的对象的表示5
05该five对象的wtf属性分配的字符串值'potato'
06 是赋值的返回值,应该是自解释的.
07该five对象的wtf属性被检查
08如five.wtf原先设置为'potato'返回'potato'这里
09的five对象是由原始值乘以5.这与任何其他对象没有区别,并在ES5规范的第11.5节中进行了解释.特别值得注意的是如何将对象转换为数值,这将在几个部分中介绍.
- 让primValue为ToPrimitive(输入参数,提示号).
- 返回ToNumber(primValue).
返回Object的默认值.通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值.对于8.12.8中的所有本机ECMAScript对象,此规范定义了[[DefaultValue]]内部方法的行为.
设valueOf是使用参数"valueOf"调用对象O的[[Get]]内部方法的结果.
如果IsCallable(valueOf)为真,那么,
- 设val是调用valueOf的[[Call]]内部方法的结果,其中O为此值和空参数列表.
- 如果val是原始值,则返回val.
这是一种迂回的说法,即调用对象的valueOf函数,并在等式中使用该函数的返回值.如果要更改valueOf功能,可以更改操作结果:
> five.valueOf = function () { return 10 }
undefined
> five * 5
50
Run Code Online (Sandbox Code Playgroud)
10当fives valueOf函数未更改时,它返回包装的原始值,5以便five * 5计算5 * 5结果25
11该five对象的wtf属性被评估一次,尽管已经从当它被分配上保持不变05.
12 'potato'
13该后缀递增操作上调用five,它得到的数值(5,我们讨论了如何更早),存储,以便它可以被返回的值,增加1的值(6),并将值five,并返回存储的值(5)
14 与之前一样,返回值是增加之前的值
15访问存储在变量wtf中的原始值(6)的属性five.ES5规范的第15.7.5节定义了这种行为.数字从中获取属性Number.prototype.
16 Number.prototype没有wtf属性,所以undefined返回
17 five.wtf被赋值为'potato?'.分配在ES5规范的11.13.1中定义.基本上,返回的值是返回但不存储.
18 'potato?' 由赋值运算符返回
19再次five,它具有6访问的值,并且再次Number.prototype没有wtf属性
20 undefined 如上所述
21 five 被访问
22 6 按照说明返回 13
Cal*_*ton 18
这很简单.
function dis () { return this; }
Run Code Online (Sandbox Code Playgroud)
这将返回this上下文.所以,如果你这样做,call(5)你将数字作为对象传递.
该call函数不提供参数,您给出的第一个参数是上下文this.通常如果你想要它在上下文中,你就{}这样给它dis.call({}),这意味着this在函数中是空的this.但是,如果你传递5它似乎它将被转换为一个对象.见.call
所以回报是 object
当你这样做时five * 5,JavaScript将对象five视为基本类型,因此等同于5 * 5.有趣的是,确实如此'5' * 5,它仍然是平等的25,所以JavaScript显然是在引擎盖下.此行没有对基础five类型进行任何更改
但是当你这样做++时会将对象转换为基本number类型,从而删除.wtf属性.因为您正在影响基础类型
Tec*_*niv 11
原始值不能具有属性.但是当您尝试访问原始值的属性时,它会透明地转换为临时Number对象.
所以:
> function dis() { return this }
undefined
// Like five.dis(), so dis return the temporaty Number object and
// reference it in five
> five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
// Write the wtf attribut on the Number object referenced by five
> five.wtf = 'potato'
"potato"
// Read the wtf attribut on the Number object referenced by five
> five.wtf
"potato"
// Return 5*5 but dont change the reference of five
> five * 5
25
// Read the same wtf attribut on the Number object referenced by five
> five.wtf
"potato"
// Change the five reference to a new primitive value (5+1). Five
// reference a primitive now.
> five++
5
// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined
// Write the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. But this object not referenced by
// five. It will be lost.
> five.wtf = 'potato?'
"potato?"
// Read the wtf attribut on a new temporary Number object construct from
// the primitive referenced by five. So wtf does not exist.
> five.wtf
undefined
> five
6
Run Code Online (Sandbox Code Playgroud)
声明功能dis.函数返回其上下文
function dis() { return this }
undefined
Run Code Online (Sandbox Code Playgroud)
dis用上下文调用5.当在严格模式(MDN)中作为上下文传递时,原始值被加框.所以five现在是对象(盒装数字).
five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
Run Code Online (Sandbox Code Playgroud)
wtf在five变量上声明属性
five.wtf = 'potato'
"potato"
Run Code Online (Sandbox Code Playgroud)
的价值 five.wtf
five.wtf
"potato"
Run Code Online (Sandbox Code Playgroud)
five是盒装的5,所以它的数量和对象是同时的(5*5 = 25).它没有变化five.
five * 5
25
Run Code Online (Sandbox Code Playgroud)
的价值 five.wtf
five.wtf
"potato"
Run Code Online (Sandbox Code Playgroud)
five在这里取消装箱.five现在只是原始的number.它打印5,然后添加1到five.
five++
5
Run Code Online (Sandbox Code Playgroud)
five6现在是原始数字,它没有属性.
five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
原语不能有属性,你不能设置它
five.wtf = 'potato?'
"potato?"
Run Code Online (Sandbox Code Playgroud)
你不能读这个,因为它没有设置
five.wtf
undefined
Run Code Online (Sandbox Code Playgroud)
five是6因为上面的后增量
five
6
Run Code Online (Sandbox Code Playgroud)
首先,它看起来像是通过nodejs控制台运行.
1.
function dis() { return this }
Run Code Online (Sandbox Code Playgroud)
创建函数dis(),但因为它没有设置为var没有返回的值所以undefined即使dis()已定义,也是输出.在旁注中,this由于未执行该功能,因此未返回.
2.
five = dis.call(5)
Run Code Online (Sandbox Code Playgroud)
这将返回javascript的Number对象,因为您只需将函数dis()的this值设置为基元5.
3.
five.wtf = 'potato'
Run Code Online (Sandbox Code Playgroud)
第一个返回"potato",因为你设置的属性wtf的five来'potato'.Javascript返回您设置的变量的值,可以轻松链接多个变量并将它们设置为相同的值,如下所示:a = b = c = 2.
4.
five * 5
Run Code Online (Sandbox Code Playgroud)
这将返回25因为你只是乘以原始号码5来five.值five由Number对象的值确定.
5.
five.wtf
Run Code Online (Sandbox Code Playgroud)
之前我跳过了这一行,因为我会在这里重复一遍.它只返回wtf您在上面设置的属性的值.
6.
five++
Run Code Online (Sandbox Code Playgroud)
正如@Callum所说,++将类型转换为number来自对象的相同值Number {[[PrimitiveValue]]: 5}}.
现在因为five是a number,你不能再设置它的属性,直到你做这样的事情:
five = dis.call(five)
five.wtf = "potato?"
Run Code Online (Sandbox Code Playgroud)
要么
five = { value: 6, wtf: "potato?" }
Run Code Online (Sandbox Code Playgroud)
另请注意,第二种方式与使用第一种方法的行为不同,因为它定义的是通用对象,而不是Number之前创建的对象.
我希望这会有所帮助,javascript喜欢假设,所以当从Number对象转换为原语时会让人感到困惑number.你可以通过使用typeof关键字检查什么类型的东西,在初始化它之后写入五个类型返回'object',然后five++返回'number'.
@deceze非常好地描述了Number对象和原始数字之间的区别.
JavaScript范围由执行上下文组成.每个执行上下文都有一个词法环境(外部/全局范围值),一个变量环境(本地范围的值)和一个这个绑定.
在此绑定是执行上下文的一个非常重要的组成部分.使用call是改变此绑定的一种方法,这样做会自动创建一个对象来填充绑定.
Function.prototype.call()(来自MDN)
句法
fun.call(thisArg[, arg1[, arg2[, ...]]])thisArg
为乐趣调用提供的值.请注意,这可能不是方法看到的实际值:如果方法是非严格模式代码中的函数,则null和undefined将替换为全局对象,并且原始值将转换为对象.(强调我的)
一旦很明显5被转换成new Number(5),其余的应该是相当明显的.请注意,只要它们是原始值,其他示例也将起作用.
function primitiveToObject(prim){
return dis.call(prim);
}
function dis(){ return this; }
//existing example
console.log(primitiveToObject(5));
//Infinity
console.log(primitiveToObject(1/0));
//bool
console.log(primitiveToObject(1>0));
//string
console.log(primitiveToObject("hello world"));Run Code Online (Sandbox Code Playgroud)
<img src="http://i.stack.imgur.com/MUyRV.png" />Run Code Online (Sandbox Code Playgroud)