我正在Javascript使用firefox scratchpad来执行它.我有一个全局索引,我想在我的内部setTimeout(或任何异步执行的函数)中获取.我无法使用,Array.push因为数据的顺序必须保持,就像它是按顺序执行一样.这是我的代码: -
function Demo() {
this.arr = [];
this.counter = 0;
this.setMember = function() {
var self = this;
for(; this.counter < 10; this.counter++){
var index = this.counter;
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
}
};
this.logMember = function() {
console.log(this.arr);
};
}
var d = new Demo();
d.setMember();
setTimeout(function(){
d.logMember();
}, 1000);
Run Code Online (Sandbox Code Playgroud)
在这里,我希望我d.arr有0到9个索引,都有'I am John!',但只有第9个索引'I am John!'.我想,保存this.counter到index局部变量会拍摄快照this.counter.有人可以帮我理解我的代码有什么问题吗?
这种情况下的问题与JS中的作用域有关.由于没有块范围,它基本上相当于
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
}
};
Run Code Online (Sandbox Code Playgroud)
当然,由于赋值是异步的,循环将运行完成,将索引设置为9.然后该函数将在100ms后执行10次.
有几种方法可以做到这一点:
IIFE(立即调用函数表达式)+闭包
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout((function (i) {
return function(){
self.arr[i] = 'I am John!';
}
})(index), 100);
}
};
Run Code Online (Sandbox Code Playgroud)
这里我们创建一个匿名函数,立即用索引调用它,然后返回一个将执行赋值的函数.当前值index保存i在闭包范围内,并且赋值是正确的
类似于1,但使用单独的方法
this.createAssignmentCallback = function (index) {
var self = this;
return function () {
self.arr[index] = 'I am John!';
};
};
this.setMember = function() {
var self = this;
var index;
for(; this.counter < 10; this.counter++){
index = this.counter;
setTimeout(this.createAssignmentCallback(index), 100);
}
};
Run Code Online (Sandbox Code Playgroud)this.setMember = function() {
for(; this.counter < 10; this.counter++){
setTimeout(function(i){
this.arr[i] = 'I am John!';
}.bind(this, this.counter), 100);
}
};
Run Code Online (Sandbox Code Playgroud)
由于我们所关心的只是正确地i进入函数,我们可以使用第二个参数bind,它部分地应用一个函数来确保稍后用当前索引调用它.我们也可以摆脱这self = this条线,因为我们可以直接绑定被this调用函数的值.我们当然也可以摆脱索引变量并this.counter直接使用,使其更加简洁.
我个人认为第三种解决方案是最好的.它简洁,优雅,完全符合我们的需要.其他一切都更难以完成语言当时不支持的事情.既然我们有bind,没有更好的方法来解决这个问题.
在setTimeout不具备的快照index,你期待。所有超时都会将索引视为最终迭代,因为您的循环在超时触发之前完成。您可以将它包装在一个闭包中并传入索引,这意味着闭包中的索引不受对 global 的任何更改的保护index。
(function(index){
setTimeout(function(){
self.arr[index] = 'I am John!';
}, 100);
})(index);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7202 次 |
| 最近记录: |