一尘不染

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

javascript

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

var a=0; //1

…这条路:

a=0; //2

…要么:

window.a=0; //3

在全球范围内?


阅读 322

收藏
2020-04-25

共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

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

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

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


#1.1let a = 0;

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

用规范术语来说,它在声明性环境记录上为全局环境而不是对象环境记录创建一个标识符绑定。全球环境是具有开裂环境记录,一个对所有旧的东西就是那张在全局对象(独特的 对象
为所有新的东西,环境记录),另一个(letconst,和所创造的功能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在NodeJS上)在全局对象上显式创建一个属性。由于它是普通属性,因此可以将其删除。

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


#4this.a = 0;

就像#3一样,除了我们通过引用全局对象this而不是引用globalwindow。但是,这在严格模式下将不起作用,因为在严格模式下,全局代码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"

现场示例:

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提升”,因为varfoo零件已移动(“提升”)到示波器的顶部,但foo = "f"零件仍保留在其原始位置。


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

letconst区别的其他两种方式与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`
    }
})();

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

以这种方式使用的函数通常称为作用域函数。在作用域函数定义的,因为他们是作用域函数可以访问的变量中定义的函数关闭了数据

2020-04-25