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

2022-08-30 00:04:30

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

var a=0; //1

...这边:

a=0; //2

...艺术

window.a=0; //3

在全球范围内?


答案 1

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

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

这将创建一个全局变量,该变量也是全局对象的属性,我们可以像在浏览器上一样访问它(或在非严格代码中通过全局作用域访问)。与某些其他属性不同,无法通过 删除该属性。windowthisdelete

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

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

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


#1.1 让 a = 0;

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

在规范术语中,它为全局环境(而不是对象环境记录)创建绑定在声明性环境记录上的标识符。全局环境的独特之处在于具有拆分的环境记录,一个用于全局对象(对象环境记录)上的所有旧内容,另一个用于不位于全局对象上的所有新内容(,以及 创建的函数)。letconstclass

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


#1.2 const a = 0;

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

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

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

#2 a = 0;

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

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


#3 窗口.a = 0;

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

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


#4 this.a = 0;

与 #3 完全相同,只是我们通过而不是全局引用全局对象。但是,这在严格模式下不起作用,因为在严格模式下,全局代码没有对全局对象的引用(它具有值)。thiswindowthisundefined


删除属性

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

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从对象中完全删除属性。你不能通过 间接添加的属性来做到这一点,要么被静默忽略,要么抛出一个异常(取决于JavaScript实现以及你是否处于严格模式)。windowvardelete

警告:IE8再次(可能更早,以及处于中断的“兼容”模式的IE9-IE11):它不会允许您删除对象的属性,即使应该允许您这样做。更糟糕的是,当您尝试时,它会引发异常(在IE8和其他浏览器中尝试此实验)。因此,从对象中删除时,您必须具有防御性:windowwindow

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

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

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


何时发生var

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

这可能会令人困惑,所以让我们来看看:

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"

示例示例:

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

如您所见,符号在第一行之前定义,但符号不是。在语句所在的地方,实际上有两件事:定义符号,这发生在第一行代码运行之前;并对该符号进行赋值,这发生在分步流中的线的位置。这被称为“吊装”,因为零件被移动(“吊装”)到范围的顶部,但零件留在其原始位置。(请参阅我贫血的小博客上可怜的误解的varfoobarvar foo = "f";varvar foofoo = "f"


何时发生letconst

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

因此,当它运行时:

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

这将引发错误:

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

与 问题并不真正相关的另外两种方式是:letconstvar

  1. var始终应用于整个执行上下文(整个全局代码,或出现它的函数中的整个函数代码),但仅在它们出现的内应用。也就是说,具有功能(或全局)作用域,但具有块作用域。letconstvarletconst

  2. 在同一上下文中重复是无害的,但是如果您有(或),则具有另一个或a或a是语法错误。var alet aconst alet aconst avar a

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

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

请注意,第二个失败,而不是从块外部访问。console.loga


题外话:避免使全局对象混乱 (window)

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

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

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

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

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


答案 2

保持简单 :

a = 0

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

var a = 0;

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

window.a = 0;

这通常与全局变量相同。