闭包中this指向问题

以下是简单的代码:

var name = " Window";
var o = {

name : "Object",
getName : function(){
    return function(){
        return this.name;
    };
}

};
var a = o.getName;
a()();
var c = o.getName()();
var o1 = {

name : "My Object",
getName : function(){
    var that = this;
    return function(){
        return that.name;
    };
}

};
var d = o1.getName()();
var e = o1.getName;
e()();

请问1 :var c = o.getName()()中在运行o.getName()时,this的指向还是o,为什么o.getName()()时,this的指向就成了window?
请问2 :var a = o.getName;a()();这种方式和o.getName()()有什么区别?

阅读 3.5k
3 个回答

当一个函数作为方法调用时,其this指针指向调用它的对象,否则this为undefined(strict mode)或window(浏览器),global(node)

o.getName()()中,getName作为o的方法调用,其this指针就指向了o,所以o.getName()执行时this指向o。
然后o.getName()返回一个函数,o.getName()()就等价于(o.getName())(),可以看到新的函数没有作为任何一个对象的方法调用,只是孤立的作为函数调用,其this指针就指向window。

如果像var a = o.getName; a()();这样将getName提取出来再调用,赋值给a的只是getName的函数本身,并不包括调用它的环境。a()运行时a只是一个函数,并没有作为任何对象的方法,其this指针就已经指向window了。

1.o.getName()()
拆开是(o.getName())()
第一步是o.getName(),是对对象方法的调用,this指向对象。
第二步是(function()return this.name;};)()对匿名函数的直接调用,非严格模式this就是window。
2.var a = 后面语句的返回值,即将=右侧语句执行的返回值赋值给a。
o.getName()()就是调用一下,没有接收返回值。
两者目的不一样。

写在前面

先要了解词法作用域 动态作用域 箭头函数,才能明白 this本质。

词法作用域 动态作用域

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 25

词法作用域(lexical scope)等同于静态作用域(static scope)。所谓的词法作用域其实是指作用域在词法解析阶段既确定了,不会改变。

箭头函数

  • 箭头函数就是个简写形式的函数表达式,并且它拥有词法作用域的this值(即不会新产生自己作用域下的this, arguments, super 和 new.target 等对象)。此外,箭头函数总是匿名的。

  • 箭头函数则会捕获其所在上下文的 this 值,作为自己的 this 值,因此下面的代码将如期运行。

  • 使用 new 操作符 (箭头函数不能用作构造器,和 new 一起用就会抛出错误。)

箭头函数,就是没有自身this属性的函数。直接在它的静态作用域找最近的函数的this.
非严格模式,函数this初始化为window/global;严格模式,函数初始化为undefined.(当然,普通函数this有绑定(指向引用)时,内部的箭头函数this指向便是此引用)

我想说啥,其实是 箭头函数只会沿着它的作用链找到最近的普通函数为止。(因为有this初始化啊)

有空也可以看看这个

C程序函数栈作用机理

进入函数时,最右边的参数arg2先入栈,按照C函数的值传递特性,此时传入的是string的副本,即arg2也是一个地址,指 向0xCFFFFFF0。然后arg1入栈,接着是返回地址入栈。因为arg2是4个字节,arg1也是一个字符串常量的地址,也是4个字 节。可以看到,此时的0xCFFFFFF0地址已经被返回地址覆盖掉了,而这个地址正是上次调用时的数组p的起始位置,并且 main中的局部变量string和printf的第二个参数arg2都指向这个地址,但此时该地址中的的值已经不是'h'了,同样的,因 为printf要为其局部变量分配内存,hello world的12个字节全部被覆写。

综上所述,printf在一进入的瞬间,哪怕不执行任何代码,原hello world的空间就被覆盖了,自然也不会得到正确的输 出。得到的全是随机的乱码。实际上也不能简单说是随机的,因为返回地址,printf的局部变量都是确定的,只是把这些 地址,局部变量都当成char输出时,肯定是乱码了,但肯定是确定的乱码

javascript 执行环境,变量对象,作用域链

  1. 作用域链Scope其实就是对执行上下文EC中的变量对象VO|AO有序访问的链表

  2. 一系列活动的执行上下文从逻辑上形成一个

JS 同时靠上述两个 数据结构 执行。

推荐问题
宣传栏