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

IT技术 javascript
2021-02-08 15:21:11

声明变量有什么区别:

var a=0; //1

...这边走:

a=0; //2

...或者:

window.a=0; //3

在全球范围内?

5个回答

是的,有一些差异,但实际上它们通常不是很大。

还有第四种方式,从 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;

这将创建一个全局变量,它也是global object 的一个属性,我们可以像window在浏览器上一样访问它(或通过this全局范围,在非严格代码中)。与其他一些属性不同,该属性不能通过 删除delete

在规范方面,它全局环境对象环境记录创建标识符绑定这使它成为全局对象的一个​​属性,因为全局对象是保存全局环境对象环境记录的标识符绑定的地方。这就是属性不可删除的原因:它不仅仅是一个简单的属性,它还是一个标识符绑定。

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

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


#1.1 let a = 0;

这将创建一个全局变量,它不是全局对象的属性。这是 ES2015 的新事物。

在规范方面,它为全局环境而不是对象环境记录声明性环境记录上创建标识符绑定全球环境是具有开裂环境记录,一个对所有旧的东西就是那张在全局对象(独特的对象为所有新的东西,环境记录),另一个(,和所创造的功能)不继续全局对象。letconstclass

绑定是执行其封闭块中的任何分步代码之前(在这种情况下,在任何全局代码运行之前)创建的,但在分步执行到达语句之前无法以任何方式访问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 显式地在全局对象上创建一个属性(在浏览器上;一些非浏览器环境具有等效的全局变量,例如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与问题相关的方式是,尽管它们定义的绑定是在任何分步代码运行之前创建的,但在到达letorconst语句之前无法访问

所以当它运行时:

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或 aconst a或 avar a是语法错误。

这是一个示例,演示了这一点,letconst在该块中的任何代码运行之前立即在其块中生效,但在letorconst语句之前无法访问

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

在那个例子中,我们定义了一个函数并让它立即执行(()最后)。

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

@ezpresso:不,是的。它们确实成为对象的属性(它们出现ExecutionContextVariableEnvironmentEnvironmentRecord;详细信息在这里这里),但是没有办法从程序代码直接访问该对象。
2021-03-20 15:21:11
@Sherlock_HJ:我添加了“在浏览器上”;这也在答案中较早,但我添加了它以防人们跳到那个。现在在规范中虽然它只是顺便说一句,但您不会找到不这样做的浏览器。我有点惊讶它不在附件 B 中
2021-03-23 15:21:11
我可以做些什么window['a']=0来说明我正在使用窗口作为地图吗?window特殊的,以至于某些浏览器不允许这样做并强迫我使用window.a
2021-03-25 15:21:11
@TJCrowder,因此,声明为的全局变量var a = 0;自动成为全局对象的属性。如果我var b = 0;在函数声明中声明,它是否也是某个底层对象的属性?
2021-04-01 15:21:11
关于#3 的一个注释可能值得澄清:window.a = 0;仅适用于浏览器环境,并且仅适用于约定。将全局对象绑定到名为变量的变量window不在 ES 规范中,因此不适用于例如 V8 或 Node.js,而this.a = 0;(在全局执行上下文中调用时)将在任何环境中工作,因为规范确实指定必须有一个全局对象。如果像Off-topic部分那样将代码包装在 IIFE 中,则可以将其this作为参数传递windowglobal获取对全局对象的直接引用。
2021-04-07 15:21:11

保持简单:

a = 0

上面的代码给出了一个全局作用域变量

var a = 0;

这段代码将给出一个在当前作用域中使用的变量,在它之下

window.a = 0;

这通常与全局变量相同。

您的发言“上面的代码提供了一个全球范围内变量”“这段代码将给当前范围内要使用的变量,并在其下”,综合考虑,建议你可以不使用第一线和接入a 的当前范围。你可以。此外,您对“全局变量”的使用有点偏离 - 您说“全局变量”的两个地方并不比您没有说的地方更具全局性。
2021-03-12 15:21:11
@Umair:“全局本身意味着您可以在任何地方访问/读取/写入变量”对。同样,您似乎将第一个和最后一个称为比中间更“全局”的,当然它们不是。
2021-03-17 15:21:11
中间的被认为是在函数内部使用,如果在主作用域下使用,它们都是相同的。在函数内使用 var 是我的假设
2021-04-02 15:21:11
global 本身意味着您可以在任何地方访问/读取/写入变量,包括我提到当前范围的地方,这很明显。如果你建议 window.a 和 'a' 在脚本中不是全局的,那么你就 100% 错了。
2021-04-03 15:21:11
@Umair:“在函数内使用 var 是我的假设”啊,好的。但这不是问题。这个问题非常清楚地说“在全球范围内”如果您要更改假设(这足够公平,以扩展和解释更一般的观点),您需要清楚这就是您在答案中所做的。
2021-04-09 15:21:11
<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.*声明的明确指南这个声明看起来最安全,不会复制粘贴你的代码,也很清楚。
2021-03-19 15:21:11

基于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];
        }
    };
}());

脚本.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希望有帮助!

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

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

还可以使用闭包来完全避免编辑全局作用域

(function() {
   // do stuff locally

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

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

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

所以坚持 window.globalName = someLocalpointer

@TJ Crowder 我忘了提到var跳转到范围的停止。
2021-03-16 15:21:11
对。如果您使用var. 它们只是完全不同的机制,具有相同的实际结果。:-)
2021-03-25 15:21:11
“在全局范围内没有语义差异。” 实际上,存在巨大的语义差异,定义属性的机制完全不同——但实际上,它归结为只有很小的实际差异(因为你不能deletea var)。
2021-04-02 15:21:11
@TJ Crowder 我不知道。我认为变量声明是在变量对象上设置属性。不知道那些不能删除。
2021-04-05 15:21:11