簡體   English   中英

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

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

有人告訴我,全局范圍中的“ this”表示全局對象,而在全局范圍中使用“ var”將定義全局對象中的屬性。 但是,我不知道為什么在node.js程序中使用“ var”和“ this”時會出現以下行為。 我發現與此類似的stackoverflow問題 ,但我認為我的問題與吊裝無關。 有人可以解釋嗎? 謝謝。 (我使用的是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();

我將上面的代碼包裝到一個HTML文件中,如下所示,並在Chrome(版本44.0.2403.157 m)中進行測試。 結果均符合預期。 我在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>

=============更新=============

在深入研究了node.js源代碼之后,我想我已經理解了示例代碼產生此類輸出的原因。 簡而言之,當node.js與“ node.js xxx.js”啟動xxx.js時,node.js將以“模塊加載”方式加載xxx.js。

1)在src / node.js中的node.js源代碼中:

如果啟動“ node xxx.js”形式的javascript文件,則node將通過使用Module.runMain()執行js文件。

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

2)在lib / module.js中:

此文件中的調用流為:“ 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);
}

由於“ _compile()”函數位於Module對象的原型結構中,因此“ _compile()”中的“ self”(或“ this”)變量必須是Module本身。 因此,“ node xxx.js”的整個javascript文件加載過程就像執行一個函數,該函數的內容為xxx.js,而該函數中的“ this”為“ Module.exports” (因為Module.exports是_compile()末尾傳遞給“ apply()”函數的第一個參數)

因此,整個加載過程等效於以下javascript代碼:

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

知道這一點之后,便可以使用以下2條簡單而基本的javascript規則來理解原始示例代碼:

  1. 如果在“ xxx();”中調用該函數,則該函數中的“ this”對象將是“ global object”。 形式而不是“ y.xxx();” 形成。

  2. 將值分配給未聲明的變量會隱式地將其創建為全局變量(它成為全局對象的屬性)。

因此,示例代碼的詳細解釋逐行如下:

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();

這個問題有點誤導,因為在全局上下文中,所有四對返回的結果都在瀏覽器的Node和(替換global window )中都相同。 這是因為var在全局范圍內沒有意義,並在this范圍內定義屬性; 由於this是全局對象,因此this.nnnglobal.zzzyyy都還定義了global屬性。 讀取時, 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> 

但是,將其包含在某些非全局范圍內,並且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> 

this.xxx / xxx部分外,其他所有內容均保持不變。 this.xxx應該是undefined ,因為xxx是局部變量,不能像其他三個變量一樣在全局對象上定義。 但是,作為局部變量,它會被該范圍內創建的任何函數所捕獲:我們說“ vxxx關閉”,或者“ v是在xxx上的閉合”,因此可以從v看到局部變量xxx

實際上,即使更酷,閉包也意味着在創建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> 

請注意,即使我們調用函數v的地方對xxx並不了解,在這里我們仍然可以將xxx讀為v內的10

注意,在Node中獲取全局范圍的唯一方法是交互式執行程序( node < stuff.js ); 如果將其作為文件( 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