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


292

声明变量之间有什么区别:

var a=0; //1

...这条路:

a=0; //2

...要么:

window.a=0; //3

在全球范围内?


2
AFAIK var a = 0; 通过另一个js文件中声明的另一个外部js文件访问变量时,在IE中不起作用
Aivan Monceller 2011年

我不知道window.a,但是其他两种方式在全局范围内是相同的。
程序员

1
@AivanMonceller真的吗?请链接。
雷诺斯

@Raynos,我在自己的网站上体验过。IE6具体。我无法让我的var枚举出现在外部js文件上,并且我将其作为html文件上的内联JavaScript引用
Aivan Monceller 2011年

@Ashwini在全局范围内,window是全局对象(在浏览器中)。var a = 1; console.log(a); console.log(win
leebriggs 2011年

Answers:


557

是的,有一些差异,尽管实际上它们通常并不大。

还有第四种方法,从ES2015(ES6)开始,还有另外两种方法。我在最后添加了第四种方式,但是在#1之后插入了ES2015方式(您会看到原因),因此我们有:

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在浏览器上(或通过this非严格代码通过全局作用域访问)访问该全局对象。与其他某些属性不同,该属性无法通过删除delete

用规范的术语来说,它在全局环境对象Environment Record上创建一个标识符绑定。这使它成为全局对象的属性,因为全局对象是保存全局环境对象环境记录的标识符绑定的地方。这就是为什么该属性不可删除的原因:它不仅是一个简单的属性,还是一个标识符绑定。

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

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


#1.1 let a = 0;

这将创建一个不是全局对象的属性。从ES2015开始这是新事物。

用规范术语来说,它在声明性环境记录上为全局环境而不是对象环境记录创建标识符绑定。全局环境的独特之处在于它具有分开的环境记录,该记录用于全局对象(该对象为所有新的东西,环境记录),另一个(letconst,和所创造的功能class)不继续使用全局对象。

绑定是在其封闭块中的任何分步代码执行之前创建的(在这种情况下,在任何全局代码运行之前),但是在分步执行到达该语句之前,它无法以任何方式访问let。一旦执行到达let语句,就可以访问该变量。(见“当letconst发生”的。)


#1.2 const a = 0;

创建一个全局常量,它不是全局对象的属性。

const完全一样,let除了必须提供一个初始化程序(= value一部分),并且一旦创建常量就无法更改其值。在幕后,这很像,let但是在标识符绑定上带有一个标志,表明其值无法更改。使用const可以为您做三件事:

  1. 如果您尝试分配给该常量,则使其成为解析时错误。
  2. 记录其对于其他程序员的不变性。
  3. 让JavaScript引擎在不改变的基础上进行优化。

#2 a = 0;

这将在全局对象上隐式创建一个属性。由于它是普通属性,因此可以将其删除。我建议要这样做,以后再阅读您的代码的人可能会不清楚。如果使用ES5的严格模式,则执行此操作(将其分配给不存在的变量)是错误的。使用严格模式是多种原因之一。

有趣的是,再次在IE8和更早版本上,创建的属性不可枚举(不会显示在for..in语句中)。这很奇怪,特别是在下面的#3中。


#3 window.a = 0;

这会使用window引用全局对象的全局对象(在浏览器上;某些非浏览器环境具有等效的全局变量,例如global在NodeJS上)在全局对象上显式创建一个属性。由于它是普通属性,因此可以将其删除。

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


#4 this.a = 0;

就像#3一样,除了我们通过引用全局对象this而不是引用global window。但是,这在严格模式下将不起作用,因为在严格模式下,全局代码this没有对全局对象的引用(而是具有值undefined)。


删除属性

“删除”或“删除”是什么意思a?正是这样:通过delete关键字完全删除属性:

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完全从对象中删除属性。你不能做到这一点与特性加入到window通过间接var时,delete要么悄悄地忽略或抛出一个异常(取决于JavaScript实现,以及是否你在严格模式)。

警告:IE8再次出现(可能是更早的版本,而IE9-IE11处于损坏的“兼容性”模式):window即使您允许,它也不允许您删除对象的属性。更糟糕的是,尝试时会引发异常(在IE8和其他浏览器中尝试此实验)。因此,从window对象中删除时,您必须具有防御性:

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

这会尝试删除该属性,如果抛出异常,它将做第二件事,并将属性设置为undefined

适用于该window对象,并且仅(据我所知)适用于IE8和更早的版本(或处于“兼容”模式的IE9-IE11)。其他浏览器可以删除window属性,但要遵守上述规则。


何时var发生

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

这可能会造成混淆,所以让我们看一下:

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"

现场示例:

如您所见,符号foo在第一行之前定义,但符号未定义bar。该var foo = "f";语句在哪里,实际上有两件事:定义符号,它发生在代码的第一行运行之前;并对该符号进行赋值,该操作会在分步流程中的直线处发生。这被称为“ var提升”,因为var foo零件已移动(“提升”)到示波器的顶部,但是foo = "f"零件仍保留在其原始位置。(请参阅我贫乏的var小博客上的可怜误解。)


letconst发生

let并且在几个方面const有所不同var。与该问题有关的方式是,尽管它们定义的绑定是在任何分步代码运行之前创建的,但直到到达or 语句之前,该绑定才可访问letconst

因此,在运行时:

display(a);    // undefined
var a = 0;
display(a);    // 0

这将引发错误:

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

let与并const与问题区别var不大的其他两种方式是:

  1. var始终适用于整个执行上下文(整个全局代码,或者贯穿在它出现的功能函数代码),但letconst仅在适用在那里出现。也就是说,var具有功能(或全球)的范围,但letconst有块范围。

  2. var a在相同的上下文中重复是没有害处的,但是如果您有let a(或const a),则具有另一个let a或a const a或a var a是语法错误。

这是一个示例,该示例演示了该操作letconst在该块中的任何代码运行之前立即在其块中生效,但是直到letor const语句才能访问:

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对象。)您可以使用函数包含所有代码以包含符号,并且如果您愿意,该函数可以是匿名的:

(function() {
    var a = 0; // `a` is NOT a property of `window` now

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

在该示例中,我们定义了一个函数,并立即执行了该函数(()最后是)。

以这种方式使用的函数通常称为作用域函数。在作用域函数定义的,因为他们是作用域函数可以访问的变量中定义的函数关闭了数据(见:瓶盖并不复杂在我贫血的小博客)。


window['a']=0可以做得很清楚我正在使用窗口作为地图吗?是否很window特殊,以至于某些浏览器不允许这样做,并强迫我使用window.a
Jayen'2

关于#3的一个注释可能值得澄清:window.a = 0;仅在浏览器环境中有效,并且仅按照惯例。将全局对象绑定到windowES规范中没有的名为的变量,因此将无法在例如V8或Node.js中使用,而this.a = 0;(在全局执行上下文中调用时)则可以在任何环境中使用,因为规范确实指定了必须有一个全局对象。如果按照“离题部分的方式将代码包装在IIFE中,则可以将其this作为名为window或的参数传递,global以获得对全局对象的直接引用。
Sherlock_HJ

@Sherlock_HJ:我添加了“在浏览器上”;答案也早于此,但我添加了它,以防其他人跳过。现在规格齐全 ; 虽然只是通过,但找不到不会这样做的浏览器。我很惊讶它不在附件B中
TJ Crowder

@TJCrowder,因此,用声明的全局变量var a = 0;自动成为全局对象的属性。如果我var b = 0;在函数声明中声明,它还会是某些基础对象的属性吗?
ezpresso '16

@ezpresso:不,是的。它们确实成为对象的属性(它们在其中出现的ExecutionContextVariableEnvironmentEnvironmentRecord此处此处的详细信息),但是无法从程序代码中直接访问该对象。
TJ Crowder

40

保持简单:

a = 0

上面的代码提供了一个全局范围变量

var a = 0;

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

window.a = 0;

这通常与全局变量相同。


您的陈述“上面的代码给出了一个全局范围变量”“该代码给出了一个要在当前范围内使用的变量,在下面的范围内”表明您不能使用第一行和a 下面的访问权限当前范围。您可以。另外,您对“全局变量”的使用有些偏离-您说“全局变量”的两个位置并不比您未说的地方更全局。
TJ Crowder

全局本身意味着您可以在任何地方访问/读取/写入变量,包括我提到当前作用域的地方,这一点非常明显。如果您建议window.a和'a'在脚本中不会是全局的,那么您100%错误。
Umair Jabbar'2

3
@Umair:“全局本身意味着您可以在任何地方访问/读取/写入变量”。再次,您似乎在呼吁第一个和最后一个,而不是中间的,更没有所谓的“全局”。
TJ Crowder

4
中间的一个被认为是在一个函数内部使用的,如果在主作用域下使用,则它们都是相同的。我的假设是在函数内使用var
Umair Jabbar

4
@Umair:“我在函数内使用var是我的假设”啊,好吧。但这不是问题。这个问题很清楚地说是“在全球范围内”。如果您要更改假设(这很公平,可以扩展并解释一个更笼统的观点),则需要明确这就是您所做的回答。
TJ Crowder

10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* 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声明”


非常好的探索。使用window.*声明的绝对指南。此声明看起来很安全,可以防止复制粘贴代码,也可以将其清除。
2012年

7

惊叹于TJ Crowder的出色回答:(题外话:避免混乱window

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

HTML

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

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

init.js(基于此答案

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

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

script.js

// 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。希望对您有所帮助!


5

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

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

还使用闭包来避免完全编辑全局范围

(function() {
   // do stuff locally

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

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

正如@AvianMoncellor提到的那样,存在一个IE错误 var a = foo仅声明文件范围的全局值。这是IE臭名昭著的解释器的问题。这个错误听起来确实很熟悉,所以可能是事实。

所以坚持 window.globalName = someLocalpointer


2
“在全球范围内,没有语义上的差异。” 其实,有一个巨大的语义差别,由该财产被定义的机制是完全不同的-但实际上它归结为只是一个小的实际差别(在你不能deletevar)。
TJ Crowder

@TJ Crowder我不知道。我认为变量声明是在变量对象上设置属性。不知道那些不能被删除。
雷诺斯

对。如果使用,它们也会在前面定义var。它们只是完全不同的机制,具有相同的实际结果。:-)
TJ Crowder'2

@TJ Crowder我忘了提到var跳转到范围的终点。
雷诺斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.