js一道面试题。貌似闭包,上下文,函数调用,声明,setTimeout()

遇到这样一道题,一个函数内,有两个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指针的用法

阅读 6.3k
11 个回答
setTimeout(this.shout.bind(this),2000);

楼下好些人答得乱七八糟的,我再补充一下

此题关键在于: 将 this.shout 传给 setTimeout 后,shout 的 this 就不是 aa 而是 window 了,因此要用 bind 重新绑定。

这不是调用的问题,而是在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后
var that = this;

是一个办法,但是不推荐,可读性不是特别好。

function () {}.bind(this);

比较推荐

最好用ES6的箭头函数,可以完美解决掉setTimeout里callback的这个问题

setTimeout(() => {waitAndShout(this.msg);});

@前端懂交互 的答案就是生产环境最常用的方案,我再来补充一种,适合熟悉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

bind 不错,但是注意下 bind 有兼容问题,低版本 IE 还要写兼容代码;
var that = this 或者 var self = this 是更常用的方法

如果要说考察用法的话,应该也算是考察了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已经是一个实例化的对象了,所以这个箭头函数的作用域指向它

先定义一个var self = this; 然后setTimeout(self.shout, 2000); 试下

    var that = this;
    this.waitAndShout=function(){
       
        setTimeout(that.shout,2000);
    };

在对象的某些方法里调用自己的,可以先什么一个自己的引用类似var that = this;

这个问题就在在于闭包中的this指向问题啊,闭包里的this指向的是window, 楼主可以百度一下

这种问题已经看过不下10遍了。。

在一个对象的方法内部,this是一个特殊变量,它始终指向当前对象;在<方法内部>再定义一个函数的话,其中的this会指向undefined(在非strict模式下,它重新指向全局对象window) 。

    var Obj=function(msg){
        this.msg=msg; 
        var that = this;// 修改部位
        this.shout=function(){
            alert(that.msg);// 修改部位
        };
        this.waitAndShout=function(){
            setTimeout(that.shout,2000);// 修改部位
        };
    }

缓存一下this

推荐问题
宣传栏