Javascript中的变量声明语法之间的区别(包括全局变量)?

Difference between variable declaration syntaxes in Javascript (including global variables)?

声明变量之间是否有任何区别:

1
var a=0; //1

……这样:

1
a=0; //2

…或:

1
window.a=0; //3

在全球范围内?


是的,有一些不同,但在实际情况下,它们通常不是很大的。好的。

还有第四条路,到ES2015(ES6)还有两条。我在结尾处添加了第四种方法,但在1之后插入了ES2015方法(您将看到原因),因此我们有:好的。

1
2
3
4
5
6
var a = 0;     // 1
let a = 0;     // 1.1 (new with ES2015)
const a = 0;   // 1.2 (new with ES2015)
a = 0;         // 2
window.a = 0;  // 3
this.a = 0;    // 4

那些声明解释了

#1台var a = 0;。好的。

这就创建了一个全局变量,它也是全局对象的一个属性,我们在浏览器上(或通过一个全局范围,非严格代码)以window的形式访问它。与其他一些属性不同,该属性不能通过delete删除。好的。

在规范术语中,它在全局环境的对象环境记录上创建一个标识符绑定。这使其成为全局对象的属性,因为全局对象是保存全局环境的对象环境记录的标识符绑定的位置。这就是属性不可删除的原因:它不仅是一个简单的属性,而且是一个标识符绑定。好的。

绑定(变量)是在第一行代码运行之前定义的(请参见下面的"当var发生时")。好的。

请注意,在IE8及更早版本中,window上创建的属性不可枚举(不显示在for..in语句中)。在IE9、Chrome、Firefox和Opera中,它是可枚举的。好的。

#1.1 let a = 0;。好的。

这将创建一个不是全局对象属性的全局变量。从ES2015年开始,这是一件新鲜事。好的。

在规范术语中,它在全局环境的声明性环境记录(而不是对象环境记录)上创建标识符绑定。全球环境的独特之处在于有一个分割的环境记录,一个记录了全球对象(对象环境记录)上的所有旧内容,另一个记录了所有新内容(letconst,以及class创建的不在全球对象上的功能。好的。

绑定是在执行其封闭块中的任何逐步代码之前创建的(在本例中,是在运行任何全局代码之前),但在逐步执行到达let语句之前,无法以任何方式访问它。一旦执行到达let语句,就可以访问该变量。(见下文"当letconst发生时")。好的。

#1.2 const a = 0;。好的。

创建全局常量,该常量不是全局对象的属性。好的。

constlet完全相同,只是必须提供初始值设定项(= value部分),并且一旦常量被创建,就不能更改它的值。在封面下,它与let完全相同,但在标识符绑定上有一个标志,表示其值不能更改。使用const可以为您做三件事:好的。

  • 如果试图分配给常量,则会导致分析时间错误。
  • 为其他程序员记录其不变的特性。
  • 让javascript引擎在不会改变的基础上进行优化。
  • #2台a = 0;。好的。

    这将在全局对象上隐式创建属性。因为它是一个普通属性,所以可以删除它。我建议不要这样做,以后任何人读你的代码都不清楚。如果使用ES5的严格模式,则执行此操作(分配给不存在的变量)是一个错误。这是使用严格模式的几个原因之一。好的。

    有趣的是,在IE8和更早的版本中,创建的属性不可枚举(不会出现在for..in语句中)。这很奇怪,尤其是下面给出的3。好的。

    #3台window.a = 0;。好的。

    这会显式地在全局对象上创建一个属性,使用引用全局对象的windowglobal(在浏览器上;某些非浏览器环境具有等效的全局变量,如nodejs上的global)。因为它是一个普通属性,所以可以删除它。好的。

    此属性在IE8和早期版本以及我尝试过的其他所有浏览器上都是可枚举的。好的。

    #4台this.a = 0;。好的。

    与3完全相同,只是我们通过this而不是全局window引用全局对象。但是,这不会在严格模式下工作,因为在严格模式全局代码中,this没有对全局对象的引用(它的值为undefined)。好的。删除属性

    我所说的"删除"或"删除"是什么意思?正是这样:通过delete关键字删除属性(全部):好的。

    1
    2
    3
    4
    window.a = 0;
    display("'a' in window?" + ('a' in window)); // displays"true"
    delete window.a;
    display("'a' in window?" + ('a' in window)); // displays"false"

    delete完全从对象中移除属性。通过var间接添加到window的属性不能这样做,delete要么被静默忽略,要么抛出异常(取决于javascript实现和是否处于严格模式)。好的。

    警告:IE8(可能更早,IE9-IE11处于中断的"兼容性"模式):它不会让您删除window对象的属性,即使您应该被允许这样做。更糟糕的是,当您尝试时它抛出一个异常(在IE8和其他浏览器中尝试这个实验)。因此,当从window对象中删除时,必须采取防御措施:好的。

    1
    2
    3
    4
    5
    6
    try {
        delete window.prop;
    }
    catch (e) {
        window.prop = undefined;
    }

    这将尝试删除该属性,如果抛出异常,它将执行下一个最佳操作,并将该属性设置为undefined。好的。

    这仅适用于window对象,仅适用于(据我所知)IE8及更早版本(或处于中断的"兼容性"模式下的IE9-IE11)。其他浏览器可以根据上述规则删除window属性。好的。当var发生时

    通过var语句定义的变量是在运行执行上下文中的任何逐步代码之前创建的,因此该属性在var语句之前就存在。好的。

    这可能会让人困惑,所以让我们看看:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    display("foo in window?" + ('foo' in window)); // displays"true"
    display("window.foo =" + window.foo);          // displays"undefined"
    display("bar in window?" + ('bar' in window)); // displays"false"
    display("window.bar =" + window.bar);          // displays"undefined"
    var foo ="f";
    bar ="b";
    display("foo in window?" + ('foo' in window)); // displays"true"
    display("window.foo =" + window.foo);          // displays"f"
    display("bar in window?" + ('bar' in window)); // displays"true"
    display("window.bar =" + window.bar);          // displays"b"

    活生生的例子:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    display("foo in window?" + ('foo' in window)); // displays"true"
    display("window.foo =" + window.foo);          // displays"undefined"
    display("bar in window?" + ('bar' in window)); // displays"false"
    display("window.bar =" + window.bar);          // displays"undefined"
    var foo ="f";
    bar ="b";
    display("foo in window?" + ('foo' in window)); // displays"true"
    display("window.foo =" + window.foo);          // displays"f"
    display("bar in window?" + ('bar' in window)); // displays"true"
    display("window.bar =" + window.bar);          // displays"b"

    function display(msg) {
      var p = document.createElement('p');
      p.innerHTML = msg;
      document.body.appendChild(p);
    }

    好的。

    如您所见,符号foo是在第一行之前定义的,但符号bar不是。在var foo ="f";语句所在的位置,实际上有两件事:在运行第一行代码之前定义符号;以及对该符号进行赋值,在该行处于逐步流的位置进行赋值。这被称为"var提升",因为var foo部分被移动("提升")到范围的顶部,但foo ="f"部分保留在其原始位置。(见我贫血的小博客上可怜的误会者11〔3〕)好的。当letconst发生时

    letconst在几个方面与var不同。与问题相关的方式是,尽管它们定义的绑定是在任何逐步代码运行之前创建的,但是直到到达letconst语句后才能访问它。好的。

    因此,当这运行时:好的。

    1
    2
    3
    display(a);    // undefined
    var a = 0;
    display(a);    // 0

    这将引发一个错误:好的。

    1
    2
    3
    display(a);    // ReferenceError: a is not defined
    let a = 0;
    display(a);

    letconstvar不同的另外两种方式与问题无关,是:好的。

  • var总是适用于整个执行上下文(整个全局代码,或它出现的函数中的整个函数代码),但letconst只适用于它们出现的块中。也就是说,var具有功能(或全局)范围,而letconst具有块范围。好的。

  • 在相同的上下文中重复var a是无害的,但是如果您有let a(或const a),有另一个let aconst avar a是语法错误。好的。

  • 下面是一个例子,说明letconst在该块中的任何代码运行之前立即在其块中生效,但在letconst语句之前无法访问:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    var a = 0;
    console.log(a);
    if (true)
    {
      console.log(a); // ReferenceError: a is not defined
      let a = 1;
      console.log(a);
    }

    注意,第二个console.log失败,而不是从块外访问a。好的。主题外:避免混淆全局对象(window)

    window对象的属性变得非常非常混乱。只要可能,强烈建议不要增加混乱。相反,将您的符号包装在一个小包装中,最多将一个符号导出到window对象。(我经常不向window对象导出任何符号。)您可以使用一个函数来包含所有代码以包含符号,如果您愿意,该函数可以是匿名的:好的。

    1
    2
    3
    4
    5
    6
    7
    (function() {
        var a = 0; // `a` is NOT a property of `window` now

        function foo() {
            alert(a);   // Alerts"0", because `foo` can access `a`
        }
    })();

    在这个例子中,我们定义了一个函数并立即执行它(末尾是())。好的。

    以这种方式使用的函数通常称为作用域函数。作用域函数中定义的函数可以访问作用域函数中定义的变量,因为它们是该数据的闭包(请参见:闭包在我的贫血小博客上并不复杂)。好的。好啊。


    保持简单:

    1
    a = 0

    上面的代码给出了一个全局范围变量

    1
    var a = 0;

    此代码将给出一个在当前作用域中使用的变量,并在该变量下

    1
    window.a = 0;

    这通常与全局变量相同。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Index.html

        var varDeclaration = true;
        noVarDeclaration = true;
        window.hungOnWindow = true;
        document.hungOnDocument = true;

    <script src="external.js">

    /* external.js */

    console.info(varDeclaration == true); // could be .log, alert etc
    // returns false in IE8

    console.info(noVarDeclaration == true); // could be .log, alert etc
    // returns false in IE8

    console.info(window.hungOnWindow == true); // could be .log, alert etc
    // returns true in IE8

    console.info(document.hungOnDocument == true); // could be .log, alert etc
    // returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

    是否存在默认情况下挂起所有变量的全局对象?例如:"globals.novar声明"


    基于T.J.克劳德的出色回答:(主题外:避免把window弄乱)

    这是他的想法的一个例子:

    HTML

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>
    <html>
      <head>
        <script type="text/javascript" src="init.js">
        <script type="text/javascript">
          MYLIBRARY.init(["firstValue", 2,"thirdValue"]);
       
        <script src="script.js">
      </head>

      <body>
        Hello !
      </body>    
    </html>

    init.js(基于此答案)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var MYLIBRARY = MYLIBRARY || (function(){
        var _args = {}; // private

        return {
            init : function(Args) {
                _args = Args;
                // some other initialising
            },
            helloWorld : function(i) {
                return _args[i];
            }
        };
    }());

    JScript

    1
    2
    3
    4
    // Here you can use the values defined in the html as if it were a global variable
    var a ="Hello World" + MYLIBRARY.helloWorld(2);

    alert(a);

    这是PLNKR。希望有帮助!


    在全局范围内没有语义差异。

    但是您确实应该避免使用a=0,因为您将值设置为未声明的变量。

    也可以使用闭包来避免编辑全局范围

    1
    2
    3
    4
    5
    6
    (function() {
       // do stuff locally

       // Hoist something to global scope
       window.someGlobal = someLocal
    }());

    在绝对必要时,始终使用闭包并提升到全局范围。无论如何,您应该在大多数通信中使用异步事件处理。

    正如@avianmoncellor所提到的,var a = foo中有一个IE bug,它只声明文件范围的全局。这是Ie臭名昭著的蹩脚翻译的问题。这个bug听起来很熟悉,所以可能是真的。

    所以还是坚持用window.globalName = someLocalpointer