简体   繁体   English

在node.js程序的全局范围内使用'var'和'this'

[英]Using 'var' and 'this' in the global scope in node.js program

I have been told that the 'this' in the global scope means the global object, and using 'var' in the global scope will define properties in the global object. 有人告诉我,全局范围中的“ this”表示全局对象,而在全局范围中使用“ var”将定义全局对象中的属性。 However, I have no idea why I get the following behaviors when using 'var' and 'this' in the node.js program. 但是,我不知道为什么在node.js程序中使用“ var”和“ this”时会出现以下行为。 I find a similar stackoverflow question about this, but I don't think my question is related to the hoisting. 我发现与此类似的stackoverflow问题 ,但我认为我的问题与吊装无关。 Could someone explain it? 有人可以解释吗? Thanks. 谢谢。 (I'm using node.js version 0.12.4) (我使用的是node.js版本0.12.4)

yyy = 20;
global.zzz = 30;

var xxx = 10;
this.nnn = 40;

var v = function() {
    //console.log(global);  // <-- There is 'yyy' in the global object as expected
    console.log(this.yyy);  // <-- 20 as expected
    console.log(yyy);       // <-- 20 as expected

    //console.log(global);  // <-- There is 'zzz' in the global object as expected
    console.log(this.zzz);  // <-- 30 as expected
    console.log(zzz);       // <-- 30 as expected

    //console.log(global);  // <-- There is no 'xxx' in the global object.   WHY??? where is 'xxx' located?
    console.log(this.xxx);  // <-- undefined as expected, because there is not 'xxx' in the global object.
    console.log(xxx);       // <-- 10.   WHY??? where is 'xxx' located?

    //console.log(global);  // <-- There is no 'nnn' in the global object.  WHY??? where is 'nnn' located?
    console.log(this.nnn);  // <-- undefined as expected, because there is not 'nnn' in the global object.
    console.log(nnn);       // <-- ReferenceError: nnn is not defined.   WHY ReferenceError instead of 'undefined'???
}

v();

I wrap the above code to an HTML file, like below, and test in the Chrome (version 44.0.2403.157 m). 我将上面的代码包装到一个HTML文件中,如下所示,并在Chrome(版本44.0.2403.157 m)中进行测试。 The results are all as expected. 结果均符合预期。 Are there any important concepts I'm missing in Node.js? 我在Node.js中缺少任何重要的概念吗?

<html>
    <head></head>
    <body>
        <script>
         yyy = 20;
         window.zzz = 30;            // change global.zzz to window.zzz

         var xxx = 10;
         this.nnn = 40;

         var v = function() {
             console.log(this.yyy);  // <-- 20 as expected
             console.log(yyy);       // <-- 20 as expected

             console.log(this.zzz);  // <-- 30 as expected
             console.log(zzz);       // <-- 30 as expected

             console.log(this.xxx);  // <-- 10 as expected
             console.log(xxx);       // <-- 10 as expected

             console.log(this.nnn);  // <-- 40 as expected
             console.log(nnn);       // <-- 40 as expected
         }

         v();
        </script>
    </body>
</html>

============= Update ============== =============更新=============

After digging into the node.js source code, I think I've understood the reason why the sample code would produce such outputs. 在深入研究了node.js源代码之后,我想我已经理解了示例代码产生此类输出的原因。 Briefly speaking, when launch xxx.js by node.js with "node.js xxx.js", the xxx.js would be loaded by node.js in a "module loading" way. 简而言之,当node.js与“ node.js xxx.js”启动xxx.js时,node.js将以“模块加载”方式加载xxx.js。

1) In src/node.js in the node.js source codes: 1)在src / node.js中的node.js源代码中:

If launching javascript file with the form of "node xxx.js", node will execute the js file by using Module.runMain(); 如果启动“ node xxx.js”形式的javascript文件,则node将通过使用Module.runMain()执行js文件。

} else if (process.argv[1]) {
      ...
      } else {
        // Main entry point into most programs:
        Module.runMain();
      }

2) in lib/module.js: 2)在lib / module.js中:

The call flow in this file are: "Module.runMain()" ==> "Module._load()" ==> "Module.load()" ==> "Module._compile()" ==> "compiledWrapper.apply(self.exports, ...)" 此文件中的调用流为:“ Module.runMain()” ==>“ Module._load()” ==>“ Module.load()” ==>“ Module._compile()” ==>“ compiledWrapper .apply(self.exports,...)”

// bootstrap main module.
Module.runMain = function() {
  // Load the main module--the command line argument.
  Module._load(process.argv[1], null, true);
  ...
};

Module._load = function(request, parent, isMain) {
  ...
  try {
    module.load(filename);
    ...
  } finally {
  ...
}

Module.prototype.load = function(filename) {
  ...
  Module._extensions[extension](this, filename);
  ...
}

Module._extensions['.js'] = function(module, filename) {
  ...
  module._compile(stripBOM(content), filename);
};

NativeModule.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];

Module.prototype._compile = function(content, filename) {
  var self = this;
  ...
  // create wrapper function
  var wrapper = Module.wrap(content); // wrap in the above 
                                      // "NativeModule.wrapper"

  var compiledWrapper = runInThisContext(wrapper, { filename: filename });
  ...
  var args = [self.exports, require, self, filename, dirname];
  return compiledWrapper.apply(self.exports, args);
}

Because "_compile()" function is in the prototype structure of the Module object, the "self" (or "this") variable in "_compile()" must be the Module itself. 由于“ _compile()”函数位于Module对象的原型结构中,因此“ _compile()”中的“ self”(或“ this”)变量必须是Module本身。 Therefore, the whole javascript file loading process of "node xxx.js" is just like to execute a function in which the content of the function is xxx.js, and the "this" in that function would be the "Module.exports" (because Module.exports is the first argument passed into the "apply()" function at the end of _compile()) 因此,“ node xxx.js”的整个javascript文件加载过程就像执行一个函数,该函数的内容为xxx.js,而该函数中的“ this”为“ Module.exports” (因为Module.exports是_compile()末尾传递给“ apply()”函数的第一个参数)

Hence, the whole loading process is equivalent to the following javascript codes: 因此,整个加载过程等效于以下javascript代码:

function (exports, require, module, __filename, __dirname) {
  /* the content of xxx.js, and "this" would refer to "module.exports" */
}

After knowing this, then the original example codes could be understood with the following 2 simple and basic javascript rules: 知道这一点之后,便可以使用以下2条简单而基本的javascript规则来理解原始示例代码:

  1. The "this" object in a function would be the "global object" if the function is called in "xxx();" 如果在“ xxx();”中调用该函数,则该函数中的“ this”对象将是“ global object”。 form rather than "y.xxx();" 形式而不是“ y.xxx();” form. 形成。

  2. Assigning a value to an undeclared variable implicitly creates it as a global variable (it becomes a property of the global object). 将值分配给未声明的变量会隐式地将其创建为全局变量(它成为全局对象的属性)。

Hence, the detailed explanations of the example codes line by line are as follows: 因此,示例代码的详细解释逐行如下:

yyy = 20; // Assign a value to an undeclared variable implicitly creates
          // it as a property of the global object, hence, "yyy" would be 
          // located in the global object.

global.zzz = 30; // explicitly add a property named "zzz" in the global 
                 // object.

var xxx = 10; // "xxx" will be a local variable in the module, and is not  
              // assigned to the "module.exports". Hence, it actually acts as 
              // a local variable in the function implicitly created by 
              // node.js to load the file as a module. Due to lexical 
              // scoping rule, it can only be accessed from the function 
              // defined in this file (module).

this.nnn = 40; // This file is loaded as a module by "applying" a function
               // and pass module.exports as the first argument of apply(), 
               // hence, the "this" here would refer to "module.exports".

console.log("this === module.exports? " + (this === module.exports)); // true

console.log(module); // you can see a "exports: { nnn: 40 }" lines in the 
                     // module object

var v = function() {
    // according to the call site syntax (v();), the "this" object here would 
    // be the global object.
    console.log("this === global? " + (this === global)); // true

    console.log(this.yyy);  // <-- 20 as expected (global objects has "yyy", 
                            // and "this" refers to the global object. 
                            // Bingo~!)

    console.log(yyy);       // <-- 20 as expected (according to lexical 
                            // scoping rule, it could find "yyy" in the 
                            // global VariableObject (equals to global 
                            // object). Bingo~!)

    console.log(this.zzz);  // <-- 30 as expected (global object has "zzz", 
                            // and "this" refers to the global object. 
                            // Bingo~!)

    console.log(zzz);       // <-- 30 as expected (according to lexical 
                            // scoping rule, it could find "zzz" in the 
                            // global VariableObject (equals to global 
                            // object). Bingo~!)

    console.log(this.xxx);  // <-- undefined as expected ("xxx" is not 
                            // defined in the global object, "xxx" is just a 
                            // local variable in the function implicitly 
                            // created by node.js to load this file, and 
                            // "this" here refers to the module.exports. 
                            // Hence, "undefined")

    console.log(xxx);       // <-- 10 as expected (according to lexical 
                            // scoping rule, it could find "xxx" in the outer 
                            // environment context of this function. Bingo~!)

    console.log(this.nnn);  // <-- undefined as expected ("nnn" is actually 
                            // defined as a property of "module.exports", and 
                            // "this" here refers to the global object, 
                            // hence, "undefined")

    console.log(nnn);       // <-- ReferenceError: nnn is not defined. 
                            // (according to the lexical scoping rule, it 
                            // could not find any "variable name" equals to 
                            // "nnn" in the scope chain. Because nnn is just 
                            // the property of the module.exports, it could 
                            // not be found in the lexical scoping searching. 
                            // hence, "ReferenceError")
}

v();

The question is a bit misguided, since in global context, all four pairs return the same result, both in Node and (replacing window for global ) in a browser. 这个问题有点误导,因为在全局上下文中,所有四对返回的结果都在浏览器的Node和(替换global window )中都相同。 This is because var has no meaning in global scope, and defines properties on this ; 这是因为var在全局范围内没有意义,并在this范围内定义属性; since this is the global object, this.nnn , global.zzz and yyy all also define properties on global . 由于this是全局对象,因此this.nnnglobal.zzzyyy都还定义了global属性。 When reading, nnn , zzz and yyy are not recognised as local variables, thus they are looked up on global object ( window here instead of global so it would run in Stack Overflow instead of Node, but same principle will apply): 读取时, nnnzzzyyy不被识别为局部变量,因此它们在全局对象上查找(此处是global window而不是global window ,因此它将在Stack Overflow中运行,而不是Node,但是将应用相同的原理):

 yyy = 20; window.zzz = 30; var xxx = 10; this.nnn = 40; var v = function() { snippet.log(Object.keys(window)); snippet.log("this.yyy: " + this.yyy); snippet.log("yyy: " + yyy); snippet.log("this.zzz: " + this.zzz); snippet.log("zzz: " + zzz); snippet.log("this.xxx: " + this.xxx); snippet.log("xxx: " + xxx); snippet.log("this.nnn: " + this.nnn); snippet.log("nnn: " + nnn); }; v(); 
 <!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script> 

However, enclose that in some non-global scope, and things change for xxx - notice how it is no longer defined on the global object: 但是,将其包含在某些非全局范围内,并且xxx发生变化-请注意如何不再在全局对象上对其进行定义:

 function vv() { yyy = 20; // = this.yyy aka window.yyy window.zzz = 30; var xxx = 10; // local variable xxx this.nnn = 40; // = window.nnn var v = function() { snippet.log(Object.keys(window)); snippet.log("this.yyy: " + this.yyy); snippet.log("yyy: " + yyy); snippet.log("this.zzz: " + this.zzz); snippet.log("zzz: " + zzz); snippet.log("this.xxx: " + this.xxx); snippet.log("xxx: " + xxx); snippet.log("this.nnn: " + this.nnn); snippet.log("nnn: " + nnn); }; v(); } vv(); 
 <!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script> 

Everything stays the same except for the this.xxx / xxx part. this.xxx / xxx部分外,其他所有内容均保持不变。 this.xxx should be undefined , since xxx is a local variable, and not defined on the global object like the other three. this.xxx应该是undefined ,因为xxx是局部变量,不能像其他三个变量一样在全局对象上定义。 However, as a local variable, it is getting captured by any functions created in that scope: we say that " v closes over xxx ", or " v is a closure over xxx ", so the local variable xxx can be seen from v . 但是,作为局部变量,它会被该范围内创建的任何函数所捕获:我们说“ vxxx关闭”,或者“ v是在xxx上的闭合”,因此可以从v看到局部变量xxx

In fact, even cooler, closure means that all local variables that are visible at the scope where v is created are visible by v as well, wherever we call v from - illustrated in the following snippet: 实际上,即使更酷,闭包也意味着在创建v的作用域内可见的所有局部变量也将被v可见, 无论我们在何处调用v from-如以下代码段所示:

 var v; function vv() { yyy = 20; // = this.yyy aka window.yyy window.zzz = 30; var xxx = 10; // local variable xxx this.nnn = 40; // = window.nnn v = function() { snippet.log(Object.keys(window)); snippet.log("this.yyy: " + this.yyy); snippet.log("yyy: " + yyy); snippet.log("this.zzz: " + this.zzz); snippet.log("zzz: " + zzz); snippet.log("this.xxx: " + this.xxx); snippet.log("xxx: " + xxx); snippet.log("this.nnn: " + this.nnn); snippet.log("nnn: " + nnn); }; } vv(); // snippet.log("xxx before calling v: " + xxx); // ReferenceError v(); // snippet.log("xxx after calling v: " + xxx); // ReferenceError 
 <!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script> 

Note that here we can still read xxx as 10 inside v , even though the place where we are calling the function v has no idea about xxx . 请注意,即使我们调用函数v的地方对xxx并不了解,在这里我们仍然可以将xxx读为v内的10

Note that the only way to get the global scope in Node is to execute your program interactively ( node < stuff.js ); 注意,在Node中获取全局范围的唯一方法是交互式执行程序( node < stuff.js ); if you execute it as a file ( node stuff.js ), a file will execute in its own scope, and you will not see this effect: 如果将其作为文件( node stuff.js )执行,则文件将在其自己的范围内执行,并且您将看不到这种效果:

// a.js
var xxx = 10;
console.log(this.xxx);

$ node < a.js
10

$ node a.js
undefined

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM