遇到这样一道题,一个函数内,有两个alert函数,第二次alert需要在第一次alert2000ms后调用
1 . var Obj=function(msg){
2 . this.msg=msg;
3 . this.shout=function(){
4 . alert(this.msg);
5 . };
6 . this.waitAndShout=function(){
7 . setTimeout(this.shout,2000);
8 . };
9 . }
10. var aa=new Obj("abc");
11. aa.waitAndShout(); //2s后undefined
搜了一下答案发现无关闭包,只是有关上下文,但不是特别明白,大神求带!!!!
我最后的解决方法是在34行间插入 var this.msg= msg; 在 10.11行插入 aa.shout();
不知有没有更好的(优雅-。-)解决方法?
抱歉刚刚没描述清楚,这道题的本意应该是调用aa.waitAndShout()呼出两个间隔两秒的alert
**我想请教的是这道题要怎么改,才能达到这种效果
最后或者是我理解错了?还是就是考察this指针的用法
大家讲道理2017-04-11 10:43:23
setTimeout(this.shout.bind(this),2000);
楼下好些人答得乱七八糟的,我再补充一下
此题关键在于: 将 this.shout 传给 setTimeout 后,shout 的 this 就不是 aa 而是 window 了,因此要用 bind 重新绑定。
迷茫2017-04-11 10:43:23
这不是调用的问题,而是在setimeout的函数里面出了问题。调用是成功了,因为有输出。
在setimeout里面this会指向window对象
参考一些库啊框架啊大触一般都这样写:
var Obj=function(msg){
this.msg=msg;
var that = this;
this.shout=function(){
alert(that.msg);
};
this.waitAndShout=function(){
setTimeout(this.shout,2000);
};
}
var aa=new Obj("abc");
aa.shout();
aa.waitAndShout(); //2s后
天蓬老师2017-04-11 10:43:23
var that = this;
是一个办法,但是不推荐,可读性不是特别好。
function () {}.bind(this);
比较推荐
最好用ES6的箭头函数,可以完美解决掉setTimeout里callback的这个问题
setTimeout(() => {waitAndShout(this.msg);});
阿神2017-04-11 10:43:23
@前端懂交互 的答案就是生产环境最常用的方案,我再来补充一种,适合熟悉call方法用
var Obj=function(msg){
this.msg=msg;
this.shout=function(){
alert(this.msg);
};
this.waitAndShout=function(){
that = this;
setTimeout(function(){that.shout.call(that)},2000);
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后abc
巴扎黑2017-04-11 10:43:23
bind 不错,但是注意下 bind 有兼容问题,低版本 IE 还要写兼容代码;
var that = this 或者 var self = this 是更常用的方法
大家讲道理2017-04-11 10:43:23
如果要说考察用法的话,应该也算是考察了this吧
要知道函数在直接调用时(不使用new关键字,或者是作为对象的函数方法调用),内部的this指针默认指向window
setTimeout是一个window的全局方法,使用setTimeout则内部的上下文肯定指向window,而window是没有msg这个变量的
楼上说了很多解决方案,无非就是变更作用域
1.直接传入上下文
var Obj=function(msg){
this.msg=msg;
var _this = this;
this.shout=function(){
alert(_this.msg); // 将this手动绑定到当前作用域
};
this.waitAndShout=function(){
setTimeout(this.shout,2000);
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后abc
// 注意,这样写是没用的
var Obj=function(msg){
this.msg=msg;
var _this = this;
this.shout=function(){
alert(this.msg); // 依然指向执行环境
};
this.waitAndShout=function(){
setTimeout(_this.shout,2000); // 没有卵用
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后undefined
2.使用bind
var Obj=function(msg){
this.msg=msg;
this.shout=function(){
alert(this.msg);
};
this.waitAndShout=function(){
setTimeout(this.shout.bind(this),2000);
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后abc
bind是返回一个新的函数而不是直接执行,如果有兼容问题,可以使用apply或者call
3.使用apply或者call
var Obj=function(msg){
this.msg=msg;
this.shout=function(){
alert(this.msg);
};
this.waitAndShout=function(){
var _this = this;
setTimeout(function(){_this.shout.call(_this)},2000);
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后abc
使用apply或者call会直接执行该函数,所以要包一层,但是包了一层之后,由于外层的function其实是在全局环境下执行了,所以要确保传入的shout方法在正确的上下文里,同时绑定当前作用域进去。
4, 使用箭头函数
var Obj=function(msg){
this.msg=msg;
this.shout=function(){
alert(this.msg);
};
this.waitAndShout=function(){
setTimeout(() => {this.shout()},2000);
};
}
var aa=new Obj("abc");
aa.waitAndShout(); //2s后abc
立即执行函数始终指向当前调用对象的作用域
要注意aa已经是一个实例化的对象了,所以这个箭头函数的作用域指向它
PHP中文网2017-04-11 10:43:23
var that = this;
this.waitAndShout=function(){
setTimeout(that.shout,2000);
};
在对象的某些方法里调用自己的,可以先什么一个自己的引用类似var that = this;
黄舟2017-04-11 10:43:23
这种问题已经看过不下10遍了。。
在一个对象的方法内部,this是一个特殊变量,它始终指向当前对象;在<方法内部>再定义一个函数的话,其中的this会指向undefined(在非strict模式下,它重新指向全局对象window) 。