关于javascript:node.js模块和函数中“this”的含义

Meaning of “this” in node.js modules and functions

我有一个由require加载的javascript文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// loaded by require()

var a = this; //"this" is an empty object
this.anObject = {name:"An object"};

var aFunction = function() {
    var innerThis = this; //"this" is node global object
};

aFunction();

(function(anyParameter){
    console.log(anyParameter.anObject);
})(
    this //"this" is same having anObject. Not"global"
);

我的问题是:var a = this;中的this是一个空对象,而函数中的this语句是node.js全局对象的阴影。我知道this关键字在函数上是不同的,但我不明白为什么第一个this不等于global,而函数中的this等于global。

node.js如何在函数作用域中将global注入this,为什么不注入模块作用域?


以下是一些你必须了解的基本事实来澄清情况:

  • 在节点模块的顶层代码中,this相当于module.exports。那是你看到的空物体。

  • 在函数内部使用this时,this的值在函数每次执行之前都会重新确定,其值由函数的执行方式决定。这意味着,如果调用机制不同(例如,aFunction()aFunction.call(newThis)emitter.addEventListener("someEvent", aFunction);等),则完全相同的函数对象的两个调用可能具有不同的this值。在这种情况下,aFunction()在非严格模式下运行的函数this设置为全局对象。

  • 当javascript文件作为节点模块被required时,节点引擎在包装器函数内部运行模块代码。调用该模块包装函数时,this设置为module.exports。(回想一下,上面的函数可以使用abitrary this值运行。)

因此,您得到不同的this值,因为每个this都位于不同的函数中:第一个位于节点创建的模块包装函数的内部,第二个位于aFunction的内部。


要理解这一点,您需要理解node.js实际上是将模块代码包装到一个函数中,就像这样

1
2
3
4
5
6
7
(function (exports, require, module, __filename, __dirname) {
    var test = function(){
        console.log('From test: '  + this);
    };
    console.log(this);
    test();
});

详细的解释可以在这个答案中找到。

现在,这个被包装的函数实际上是这样调用的

1
2
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);

因此,在模块级,this实际上是exports对象。

你可以这样确认

1
2
console.log(this, this === module.exports);
// {} true


总结:

在javascript中,当调用函数时,会确定EDOCX1的值〔0〕。创建函数时不会。在模块最外层的nodejs中,this的值是当前的module.exports对象。当一个函数作为一个对象的属性被调用时,它的值将改变为它被调用的对象。您只需在点规则的左侧就可以记住这一点:

When a function is called you can determine the value of this by looking at the place of the function invocation. The object left of the dot is the value of this. If there is no object left of the dot the value of this is the module.exports object (window in browsers).

注意事项:

  • 此规则不适用于没有自己绑定thises2015箭头函数。
  • 函数callapplybind可以改变有关this值的规则。

示例(nodejs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(this);  // {} , this === module.exports which is an empty object for now

module.exports.foo = 5;

console.log(this);  // { foo:5 }

let obj = {
    func1: function () { console.log(this); },
    func2: () => { console.log(this); }
}

obj.func1();  // obj is left of the dot, so this is obj
obj.func2();  // arrow function don't have their own this
              // binding, so this is module.exports, which is{ foo:5 }

输出:

enter image description here


这是因为node.js模块中的默认全局对象是exports对象,而您正在调用test(),它没有指定this。在传统的JS中,this指向全局对象,use strict为空。

this可以指向任何东西,这取决于你如何称呼它。

    小精灵test():使用全局对象(exports作为this,除非在严格模式下,this为空;
  • test.call({})test.apply({}):您正在指定作为this使用的内容(第一个参数)
  • var obj = {testRef: test}; obj.testRef()this设置在.的左边,即obj

反驳你的回答

的确,模块顶层的thisexports,但这并不意味着test()内部的this也会指向调用它的来源。

试图证明this和global object都指向exports

1
2
 myGLobal = 5;
 this.myGlobal; // 5