最近在读一本电子书——Miku’s Node Book。读了几章,看到一些我没注意过的东西,说他是陷阱也可以。
####1.关于this关键字
the value of this is determined at the time the function is called, rather than being fixed to some particular value
首先看一段代码:
var obj = {
id: "xyz",
printId: function() {
console.log('The id is '+ this.id + ' '+ this.toString());
}
};
obj.printId();//这是第一行输出
var callback=obj.printId;
callback();//这是第二行输出
setTimeout(obj.printId,1000);//这是第三行输出
没读这书之前,很多有其他语言编程经验的人估计都会认为答案是:The id is xyz [object Object],但真实的结果是这样的
第一行输出:The id is xyz [object Object];(正常)
第二行输出:The id is undefined [object global];
第三行输出:The id is undefined [object Object];
这样就出现问题了,只有obj.printId()返回了我们想要的结果,第二中调用只是一个赋值,但是this却指向global,第三种调用,this指向Object,但是显然不是我们定义的obj,因为它获取不到id属性。原因就在于,在javascript中,this关键字的指向实在函数调用的时候定义的。callback()在调用的时候,属于全局顶层的函数,已经不是我们定义的obj中的一个函数,所以this指向global,至于setTimeout的回调函数调用方式之所以指向一个Object,这应该和setTimeout这个api的实现方式有关。
那我们有没有办法用callback和setTimeout方式显示出正常结果呢?你可以这样写:
obj.printId();//这是第一行输出
var callback=function() { obj.printId(); };
callback();//这是第二行输出
setTimeout(function() {obj.printId(); },1000);//这是第三行输出
####2.关于变量作用域
In Javascript, all functions store “a hierarchical chain of all parent variable objects, which are above the current function context; the chain is saved to the function at its creation”.
继续看例子:
第一个,最简单的例子:
for(var i = 0; i < 5; i++) {
console.log(i);
}
第二个,加个异步函数,也很简单:
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
第三个,我们制作一个方法数组:
var data = [];
for (var i = 0; i < 5; i++) {
data[i] = function foo() {
console.log(i);
};
}
data[0](); data[1](); data[2](); data[3](); data[4]();
第一个,如我们所想,输出0,1,2,3,4;可是第二个第三个都是……5,5,5,5,5.
这个是因为,我们在for循环中定义的变量i,在for循环结束后还没有走出它的作用域,也就是说for循环之后,i依然可以访问,值等于最后一次循环后的值,就是5了。第二段代码中,setTimeout的回调函数执行时,for循环已经完成,此时i就是5。同理,第三段代码,在data[i] ()执行时,i已经是5了。当然,我们有办法让它显示正确结果,但要增加一个变量,像这样:
for(var i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout( function() { console.log(j); }, 500*i);
})();
}
####bonus:在object外面增加method的方法
像这样:
var obj1 = { id: "Foo"};
var obj2 = { id: "Bar"};
function f1(a, b) {
console.log(this, a, b);
}
f1.call(obj1, 'A', 'B');
f1.apply(obj2, [ 'A', 'B' ]);
ps:代码来自 Mixu’s Node Book,你也可以在我的博客都到这篇文章。