最近的一条tweet包含了下面的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
特别是,我不清楚:
为什么dis.call(5)的结果是一个带有[[PrimitiveValue]]属性的数字,但是5 ++和5 * 5的结果似乎只是普通的数字5和25(不是数字)
为什么是五个。WTF属性在5++增量之后消失
为什么是五个。WTF属性在5++增量之后甚至不再是可设置的,尽管five。WTF =土豆?'赋值显然设置了值。
声明函数dis。函数返回其上下文
function dis() { return this }
undefined
用context 5调用dis。原始值在严格模式(MDN)中作为上下文传递时被装箱。所以5现在是object(盒装数字)
five = dis.call(5)
Number {[[PrimitiveValue]]: 5}
在五个变量上声明wtf属性
five.wtf = 'potato'
"potato"
5 .wtf的值
five.wtf
"potato"
5被框5,所以它同时是数字和对象(5 * 5 = 25)。它不会改变5。
five * 5
25
5 .wtf的值
five.wtf
"potato"
在这里打开5。5现在只是一个原始数。它输出5,然后5加1。
five++
5
5现在是原数6,它没有任何属性。
five.wtf
undefined
基本类型不能有属性,你不能设置这个
five.wtf = 'potato?'
"potato?"
你不能读这个,因为没有设置
five.wtf
undefined
5是6,因为上面的增量
five
6
JavaScript作用域由执行上下文组成。每个执行上下文都有一个词法环境(外部/全局作用域值)、一个变量环境(局部作用域值)和一个this绑定。
这个绑定是执行上下文的一个非常重要的部分。使用调用是改变这个绑定的一种方法,这样做将自动创建一个对象来填充绑定。
Function.prototype.call() (from MDN)
语法
乐趣。调用(thisArg[, arg1[, arg2[,…]]])
thisArg
这一价值提供了对乐趣的召唤。注意,这可能不是方法所看到的实际值:如果方法是一个非严格模式代码中的函数,null和undefined将被替换为全局对象,原始值将被转换为对象。(强调我的)
一旦很明显5被转换为新的数字(5),其余的应该是相当明显的。注意,其他示例也可以工作,只要它们是基本值。
函数primitiveToObject(的){
返回dis.call(的);
}
函数dis(){返回this;}
/ /现有的例子
console.log (primitiveToObject (5));
/ /∞
console.log (primitiveToObject (1/0));
/ / bool
console.log (primitiveToObject (1 > 0));
/ /字符串
console.log (primitiveToObject(“hello world”);
<img src="http://i.stack.imgur.com/MUyRV.png" />
这很简单。
function dis () { return this; }
这将返回This上下文。因此,如果调用(5),则将数字作为对象传递。
call函数不提供参数,你给出的第一个参数是this的上下文。通常,如果你想要它在context上,你给它{}diss。call({})这意味着函数中的this是空的this。然而,如果你传递5,它似乎会被转换成一个对象。看到打电话给
返回的是object
当执行5 * 5时,JavaScript将对象5视为基本类型,因此相当于5 * 5。有趣的是,执行'5' * 5,它仍然等于25,因此JavaScript显然是在底层进行强制转换。在这一行中没有对底层的5类型进行任何更改
但是当你执行++时,它会将对象转换为原始数字类型,从而删除.wtf属性。因为您正在影响底层类型
表示数字有两种不同的方式:
var a = 5;
var b = new Number(5);
第一个是原语,第二个是对象。从所有的意图和目的来看,它们的行为是相同的,除了它们在打印到控制台时看起来不同。一个重要的区别是,作为一个对象,new Number(5)像普通{}一样接受新属性,而原语5则不接受:
a.foo = 'bar'; // doesn't stick
b.foo = 'bar'; // sticks
至于初始的dis.call(5)部分,请参阅“this”关键字如何工作?假设要调用的第一个参数被用作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"
Number对象接受新的属性。
> five++
++的结果是一个新的原语6值…
> five.wtf
undefined
> five.wtf = 'potato?'
"potato?"
> five.wtf
undefined
...它不具有也不接受自定义属性。
*注意,在严格模式下,this参数将被区别对待,不会被转换为Number。参见http://es5.github.io/#x10.4.3了解实现细节。
首先,它看起来是通过nodejs控制台运行的。
1.
function dis() { return this }
创建函数dis(),但由于它没有设置为var,因此没有返回值,因此输出为undefined,即使dis()已定义。顺便说一句,这个没有返回,因为函数没有执行。
2.
five = dis.call(5)
这将返回javascript的Number对象,因为您刚刚将函数dis()的This值设置为原语5。
3.
five.wtf = 'potato'
第一个返回“potato”,因为您刚刚将属性wtf(5)设置为“potato”。Javascript返回你所设置的变量的值,这样就可以很容易地将多个变量链接起来,并将它们设置为相同的值,如下所示:a = b = c = 2。
4.
five * 5
这个返回25,因为你刚刚把原数5乘以了5。5的值由Number对象的值决定。
5.
five.wtf
我之前跳过了这一行因为我要在这里重复。
它只是返回你在上面设置的属性wtf的值。
6.
five++
正如@Callum所说,++将从对象number {[[PrimitiveValue]]: 5}}中的相同值将类型转换为数字。
现在因为5是一个数字,你不能再为它设置属性,直到你做这样的事情:
five = dis.call(five)
five.wtf = "potato?"
or
five = { value: 6, wtf: "potato?" }
还要注意,第二种方法与使用第一种方法的行为不同,因为它定义的是一个泛型对象,而不是之前创建的Number对象。
我希望这能有所帮助,javascript喜欢假设事情,所以当从数字对象更改为原始数字时,它可能会令人困惑。
你可以使用typeof关键字来检查某个东西是什么类型
typeof五
初始化后,它返回'object',执行5++后,它返回'number'。
@ desze非常好地描述了Number对象和基本数之间的区别。