js闭包题目求解

var obj = {
    a:3,
    b:function(){
        console.log(this,1);
        var that = this;
        var s = function(){
            console.log(this,2);
            console.log(that,3);
        }
        return s;
    }
}
obj.b()();

来,求大神解释一下,为什么第二个输出是全局对象。this不应该访问的是父级内的this么?


继续再追问一下

var obj = {
    a:3,
    b:function(){
        console.log(this,1);
        var that = this;
        var s = function(){
            console.log(this,2);
            console.log(that,3);
        }
        s();
    }
}
obj.b();

这种情况下,2为什么也是window呢?

阅读 3.2k
6 个回答

其实这里就是this的隐式丢失了。
一个隐式丢失的过程是这样子的:

var obj = {
    a: function() {
        console.log(this);
    }
};
obj.a(); //作为对象的方法使用,隐式绑定,this为这个对象。
var b = obj.a
b(); //赋给一个变量,发生隐式丢失,this指向window。

可以这样理解,this词法是跟调用的方式有关的,当调用obj.a()的时候,这个函数是基于obj对象来调用的,它作为对象的方法来调用,所以很显然this指向obj
var b = obj.a其实就等价于:

var b = function() {
    console.log(this);
}

于是当你调用b的时候,这是对b的直接调用,故指向window
所以再看回你的代码。
当你return了一个函数时,这里创建了一个闭包,that保存了对词法作用域的引用。
这个时候,obj.b()就等价于

function s() {
    console.log(this,2);
    console.log(that,3);
}

又因为创建了闭包,所以that能够访问得到原来的词法作用域。但是这里的this跟函数调用的方法有关,它相当于直接调用了一个s函数,所以this自然指向window
你可以去看下《你不知道的JS(上卷)》,里面this一节讲得十分清楚。


补充第二段代码解释:
其实这就是对函数的独立调用,默认绑定到了window对象上面。
刚刚的说法有点小错误。
this词法与函数调用的位置调用的方式有关。
一般情况下,都是先找到函数调用的位置,再判断调用的方式。按照你的代码,其实就是两种方式。

  • 一种是默认绑定,就是对函数的独立调用,指向window,严格模式下指向的是undefined
  • 一种是隐式绑定,就是作为对象的方法去调用。但是这种情况很多时候会出现隐式丢失。

综上,第一段代码,就是隐式丢失的情况。第二段代码,仅仅只是对于函数的独立调用,虽然是在obj中的b被调用的,仅此而已。

this不是在定义对象的时候就确定了的,this的值取决于调用的对象,obj.b()首先返回了function s,然后执行s(),这时候this已经不是obj了。

因为返回的是一个函数,函数此时是没调用的,等到下面调用的时候,这时的this是指向window的。

我的理解是return 出去的时候其实 obj.b()就是这个S 而这个S,这时的调用对象是window window.s() ,所以第二个输出了window。如有误,还请指出

obj.b() ---> function(){
            console.log(this,2);
            console.log(that,3);
        }

obj.b()() ---> function(){
            console.log(this,2);
            console.log(that,3);
        }()

代码:

var obj = {
    a:3,
    b:function(){
        console.log(this,1); // 第一个this
        var that = this;     // that
        var s = function(){
            console.log(this,2); // 第二个this
            console.log(that,3); // that
        }
        return s;
    }
}
obj.b()();

调用 obj.b()() 等价于如下:

let b = obj.b(); 
b();

obj.b()执行时:

第一个this绑定到 obj,是{a:3,b:function(){/* */}}
that被赋值第一个this,同样也是{a:3,b:function(){/* */}}
第二个this,因为没有被调用,所以保留。实际调用时再绑定。
所以这时,代码变成了下面这样:

let b = function() {
    console.log(this,2); // 第二个this
    console.log({a:3,b:function(){/*  */}},3); 
}
b();

b()执行时:

第二个this绑定到全局对象

没毛病吧,哈哈哈?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题