当我在阅读Secrets of the JavaScript Ninja
一书时,在该书的第四章末尾的Exercises
中有这样两个问题。
When running the following code, which of the assertions will pass?
function Ninja(){
this.whoAmI = () => this;
}
var ninja1 = new Ninja();
var ninja2 = {
whoAmI: ninja1.whoAmI
};
assert(ninja1.whoAmI() === ninja1, "ninja1 here?");
assert(ninja2.whoAmI() === ninja2, "ninja2 here?");
Which of the following assertions will pass?
function Ninja(){
this.whoAmI = function(){
return this;
}.bind(this);
}
var ninja1 = new Ninja();
var ninja2 = {
whoAmI: ninja1.whoAmI
};
assert(ninja1.whoAmI() === ninja1, "ninja1 here?");
assert(ninja2.whoAmI() === ninja2, "ninja2 here?");
起初,我的猜想是:
- 第一个问题的答案是:
ninja1.whoAmI() === ninja1
,ninja1.whoAmI() === window
- 第二个问题的答案是:
ninja1.whoAmI() === ninja1
,ninja2.whoAmI() === ninja1
对于第一个问题的回答,我不确定。因为书中对于arrow function
中this
的解释,我也不是很理解。原文是这样的:arrow functions don’t get their own implicit this parameter when we call them; instead they remember the value of the this parameter at the time they were created. 当我们调用arrow functions时,它们没有自己的隐式的this参数;它们只记得 在创建它们时 this参数的值
。
对于第二个问题的回答,我的根据是书中的这样一段话:
The bind method is available to all functions, and is designed to
create and return a new function that’s bound to the passed-in
object (in this case, the button object). The value of the this
parameter is always set to that object, regardless of the way the
bound function was invoked. Apart from that, the bound function
behaves like the originating function, because it has the same code
in its body.
所有的`function`都有`bind method`这个方法,该方法创建并返回
一个`新的function`,这个`新的function`被绑定到传入的对象上。
不管`新的function`是以何种方式被调用的,在该`新的function`中
`this`的值永远都是传入的对象。
除此以外,`新的function`在行为上 和原来的函数类似,
因为`新的function`和原来的函数`function body`有着同样的代码。
所以我对上面的代码加了一些assert
,并运行它。因为把问题1和问题2的测试都放在一起了,所以对问题2的代码做了些改动 以便测试。
<!DOCTYPE html>
<html>
<head>
<title>Using arrow functions to work around callback function contexts</title>
<meta charset="utf-8">
<script src="../assert.js"></script>
<link rel="stylesheet" type="text/css" href="../assert.css">
</head>
<body>
<script>
function Ninja(){
this.whoAmI = () => this;
}
var ninja1 = new Ninja();
var ninja2 = {
whoAmI: ninja1.whoAmI
};
assert(ninja1.whoAmI() === ninja1, "ninja1.whoAmI() === ninja1");
assert(ninja2.whoAmI() === ninja2, "ninja2.whoAmI() === ninja2");
assert(ninja2.whoAmI() === window, "ninja2.whoAmI() === window");
assert(ninja2.whoAmI() === ninja1, "ninja2.whoAmI() === ninja1");
assert(ninja1.whoAmI === ninja2.whoAmI, "ninja1.whoAmI === ninja2.whoAmI");
function Person(){
this.whoAmI = function(){
return this;
}.bind(this);
}
var p1 = new Person();
var p2 = {
whoAmI: p1.whoAmI
}
assert(p1.whoAmI() === p1, "p1.whoAmI() === p1");
assert(p2.whoAmI() === p2, "p2.whoAmI() === p2");
assert(p2.whoAmI() === window, "p2.whoAmI() === window");
assert(p2.whoAmI() === p1, "p2.whoAmI() === p1");
assert(p1.whoAmI === p2.whoAmI, "p1.whoAmI === p2.whoAmI");
</script>
</body>
</html>
运行结果如下图:
- 根据运行结果的第五行和第十行,可以看出
ninja1.whoAmI
和ninja2.whoAmI
是同一个函数。ninja2.whoAmI() === ninja1
这个结果在问题2中不显得那么奇怪,因为书中的话 已经解释的相当清楚了。- 但是
ninja2.whoAmI() === ninja1
这个结果在问题1中是显得有点奇怪的,因为 它和我预期的ninja2.whoAmI() === window
不一样。
所以我就搜了下,Stack Overflow中有类似的 但不完全一样的问题,但是其中的一些回答也适用于我的问题。
Arrow functions bind their do not have this
when they are createdthis
, arguments
or other special names bound at all - when the object is being created the name this
is found in the enclosing scope.
Arrow functions
没有this
arguments
这些参数。当Arrow functions
被创建时,this
参数是在enclosing scope
中被找到的。
并且下面的两段代码近似相等
function Ninja(){
this.whoAmI = () => this;
}
can be translated into a vague approximation of the arrow syntax in ES5:
function Ninja(){
this.whoAmI = function(){
return this;
}.bind(this);
}
在问题1中的结果ninja2.whoAmI() === ninja1
也就得到了解释。
所以说,书中的话并不是那么的准确:arrow functions don’t get their own implicit this parameter when we call them; instead they remember the value of the this parameter at the time they were created.
换个更准确的说法,就应该是:Arrow functions bind their do not have this
when they are createdthis
, arguments
or other special names bound at all - when the object is being created the name this
is found in the enclosing scope.
尽管解决了一些我的疑问,但是 我还是不清楚: 即,函数是何时被创建的。
通过回顾之前的一篇文章,让我对函数是合适创建的有了一些新的认识:
编译时 对所有代码进行逐行检测 。检测到所有的var(var当然也包括函数的定义,但是不包括赋值表达式右侧的函数定义),并分配存储空间。 注意是先编译后执行,编译时为所有的变量的定义 分配好存储空间(函数的定义也视作var声明,因此函数的定义 也被分配了存储空间),要区分compilation phase和execution phase。并且编译也不是一次性完成的,每当遇到要执行的函数时,会对要执行的函数进行编译(大概 函数就是在此时被创建的吧?)。因此编译和执行时交叉进行的。
转载请注明出处