为什么' this '指向闭包中的全局对象?

why does `this` refer to the global object in closure

本文关键字:全局 对象 this 为什么 闭包      更新时间:2023-09-26
var num = 1;
var obj = {
    num: 2,
    getNum: function() {
        return (function() {
            return this.num;
        })();
    }
}
console.log(obj.getNum()); // 1

JavaScript代码的结果是1。所以this指的是全局对象,而不是obj。

在其他条件相同的情况下,当直接在函数中使用时,当以obj.method()的典型方式调用该函数时,this指向作为方法(成员)的对象。Directly的意思是直接;在方法内嵌套的函数中使用,无论是匿名的还是立即调用的还是其他方式,都不符合的条件。

在你的情况下

return (function() {
    return this.num;
})();

函数不是任何对象的成员,因此没有this,因此this是全局对象(或严格模式下为null)。

仅仅因为你使用this的函数嵌套在一个函数中,这个函数是一个方法,所以不够。使用this的函数必须是方法本身。

要得到你想要的行为,你有几个选择:

getNum: function() {
  return (() => this.num)();
}

这可以工作,因为胖箭头函数使用词法this

另一个想法,已经在另一个答案中显示了:

getNum: function() {
  var self = this;
  return function() {
    return self.num;
  }();
}
顺便说一下,在这个上下文中,没有必要将函数用圆括号括起来,强制它成为一个可以立即调用的函数表达式。仅仅因为它在:的右边,它就已经是一个表达式了。

还有第三种方法:

getNum: function() {
  return function() {
    return this.num;
  }.call(this);
}

通过显式地使用call,我们将匿名函数的this强制为getNumthis

要获得明确的答案,请参阅"如何"。关键字工作?。

关于术语"闭包"的说明

在你的问题中,你似乎使用术语"闭包"来表示"嵌套函数"。虽然您的嵌套的、匿名的、立即调用的函数在某种极其狭窄的技术意义上可能是闭包,但是以这种方式使用术语可能会混淆您自己和其他人。您的函数不满足以下两个被普遍接受的闭包标准中的任何一个:

  1. 它不会"关闭"(使用)周围作用域中的任何变量。

  2. 它不会作为一个函数值返回给外部世界,同时还会从周围的词法作用域携带对这些变量的引用。

因此,简单地将此函数称为"嵌套函数"会更清楚。

在函数内部this取决于执行上下文,因此如何调用函数很重要。

如果调用上下文没有设置this,在严格模式和非严格模式下是不同的。

在非严格模式下,它引用全局对象。在严格模式下,为undefined。注意,undefined实际上可以设置为其他值。

要设置上述上下文,需要将function作为对象的属性调用,就像在obj.getNum()调用中一样。In将给函数一个对象作用域,但它与您尝试实际访问this的另一个函数调用没有任何关系。

下面的代码说明了它:

var num = 1;
var obj = {
  num: 2,
  getNum: function() {
    var outerThis = this;
    return (function() {
      return {
        innerThis: this,
        outerThis: outerThis,
        innerNum: this.num,
        outerNum: outerThis.num
      };
    })();
  },
  getNumStrict:function() {
    "use strict"
    var outerThis = this;
    return (function() {
      return {
        innerThis: this,
        outerThis: outerThis,
        outerNum: outerThis.num
      };
    })(outerThis.num);
  }
}
console.log(obj.getNum());
// Object {innerThis: Window, outerThis: Object, innerNum: 1, outerNum: 2}
console.log(obj.getNumStrict());
// Object {innerThis: undefined, outerThis: Object, outerNum: 2}

你可以看到,this在JavaScript中是一个有争议的概念,你可以很容易地没有它。

要使私有属性私有,可以在函数作用域中创建它们,并返回一个返回接口副本的函数,而不是对象引用:

var num = 1;
var createHaveNum = function(initial) {
  var num = initial;
  var getInterface;
  var setNum = function(x) {
    num = x;
    console.log("setting num");
  };
  var getNum = function() {
    console.log("getting num");
    return num;
  };
  getInterface = function() {
    return {
      getNum: getNum,
      setNum: setNum
    };
  };
  console.log("Instance is initialized.");
  return getInterface;
};
var obj = createHaveNum(2); // create an object, obtain a reference in form of a function
var i = obj(); // obtain an interface
var j = obj(); // do it again, interface to same object
j.setNum = undefined; // does not break internal state, object is intact
console.log(i.getNum());
i.setNum(3);
var getNum = i.getNum; // reference function
console.log(getNum()); // invoke without a scope, no problem
var other = {getNum: i.getNum}; // put in another object
console.log(other.getNum()); // call with another context, no problem - same scope as before.
/*
Instance is initialized.
getting num
2
setting num
getting num
3
getting num
3
*/

因为在定义对象文字时obj不存在,所以不能关闭它。最重要的是,闭包在作用域上操作,在函数之外没有作用域。

在计算字面量时,没有obj。在对该语句求值时,首先求对象字面值(必须先求=操作符的右操作数),然后赋值给新声明的变量obj

更糟糕的是,ES中的闭包是在作用域中的,而ES5(及以下版本)只有函数作用域。除了全局作用域,没有外部作用域可以关闭。

要获得"期望"的结果,您必须重新定义getName,如下所示:

getNum: function() {
    var that = this; //the enclosing object
    return (function() {
        return that['num'];
    })();
}