小编典典

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

all

声明变量有什么区别:

var a=0; //1

…这边走:

a=0; //2

…或者:

window.a=0; //3

在全球范围内?


阅读 98

收藏
2022-04-21

共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

这些陈述解释了

#1var a = 0;

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

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

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

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


#1.1let a = 0;

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

在规范方面,它为全局环境而不是 对象环境记录在 声明性 环境记录上创建标识符绑定。全局环境的独特之处在于有一个拆分的环境记录,一个用于全局对象(
对象 环境记录)上的所有旧内容,另一个用于所有不存在的新内容(,和创建的函数)继续全局对象。 let``const``class

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


#1.2const a = 0;

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

const完全一样let,只是您必须提供一个初始化程序(= value部分),并且一旦创建常量就不能更改它的值。在幕后,它完全一样let,但标识符绑定上有一个标志,表示它的值不能更改。使用const为您做三件事:

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

#2a = 0;

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

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


#3window.a = 0;

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

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


#4this.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通过间接添加的属性来做到这一点vardelete要么被静默忽略,要么抛出异常(取决于
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"

现场示例:

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"零件仍留在其原始位置。(请参阅我贫血的小博客上
被误解的可怜。)var


何时letconst发生

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

因此,当它运行时:

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具有函数(或全局)范围,但let具有const块范围。

  2. 在相同的上下文中重复var a是无害的,但如果你有let a(or const a),有另一个let aor 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`
    }
})();

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

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

2022-04-21