声明变量有什么区别:
var a=0; //1
…这边走:
a=0; //2
…或者:
window.a=0; //3
在全球范围内?
是的,有一些差异,尽管实际上它们通常不是很大的差异。
还有第四种方式,从 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;
var a = 0;
这会创建一个全局变量,它也是 全局对象 的一个属性,我们可以window在浏览器上访问它(或通过this全局范围,在非严格代码中)。与其他一些属性不同,该属性不能通过delete.
window
this
delete
在规范方面,它为 全局环境 在 对象 环境记录 上创建一个 标识符绑定 。这使它成为全局对象的属性,因为全局对象是全局环境对象环境记录的标识符绑定所在的位置。这就是该属性不可删除的原因:它不仅仅是一个简单的属性,它还是一个标识符绑定。 __
绑定(变量)在第一行代码运行之前定义(请参阅var下面的“何时发生”)。
var
请注意,在 IE8 和更早版本上,创建的属性window是不可 枚举 的(不会出现在for..in语句中)。在 IE9、Chrome、Firefox 和 Opera 中,它是可枚举的。
for..in
#1.1let a = 0;
let a = 0;
这会创建一个全局变量,它 不是 全局对象的属性。这是 ES2015 的新事物。
在规范方面,它为全局环境而不是 对象环境记录在 声明性 环境记录上创建标识符绑定。全局环境的独特之处在于有一个拆分的环境记录,一个用于全局对象( 对象 环境记录)上的所有旧内容,另一个用于所有不存在的新内容(,和创建的函数)继续全局对象。 let``const``class
let``const``class
绑定是在其封闭块中的任何逐步代码执行之前 创建 的(在这种情况下,在任何全局代码运行之前),但在逐步执行到达语句之前,它无法以任何方式 访问。let一旦执行到达let语句,变量就可以访问了。(请参阅下面的“时间let和const发生”。)
let
const
#1.2const a = 0;
const a = 0;
创建一个全局常量,它不是全局对象的属性。
const完全一样let,只是您必须提供一个初始化程序(= value部分),并且一旦创建常量就不能更改它的值。在幕后,它完全一样let,但标识符绑定上有一个标志,表示它的值不能更改。使用const为您做三件事:
= value
#2a = 0;
a = 0;
这会隐式地 在全局对象上创建一个属性。由于它是普通属性,您可以将其删除。我建议 不 要这样做,以后阅读您的代码的任何人都可能不清楚。如果你使用 ES5 的严格模式,这样做(分配给不存在的变量)是错误的。这是使用严格模式的几个原因之一。
有趣的是,同样在 IE8 和更早版本上,创建的属性不可 枚举 (不会出现在for..in语句中)。这很奇怪,特别是考虑到下面的#3。
#3window.a = 0;
window.a = 0;
这会显式地在全局对象上创建一个属性,使用window引用全局对象的 global(在浏览器上;一些非浏览器环境具有等效的全局变量,例如global在 NodeJS 上)。由于它是普通属性,您可以将其删除。
global
此属性 是 可枚举的,在 IE8 及更早版本以及我尝试过的所有其他浏览器上。
#4this.a = 0;
this.a = 0;
与 #3 完全一样,只是我们通过this而不是global 引用全局对象window。但是,这在严格模式下不起作用,因为在严格模式下,全局代码this没有对全局对象的引用(而是有值undefined)。
undefined
“删除”或“删除”是什么意思a?正是:通过delete关键字(完全)删除属性:
a
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
这可能会令人困惑,所以让我们看一下:
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
foo
bar
var foo = "f";
var foo
foo = "f"
let并且在几个方面const有所不同。var与问题相关的方式是,尽管它们定义的绑定是在任何分步代码运行之前创建的,但在到达or语句之前它是不可 访问的。let``const
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
var始终适用于整个执行上下文(整个全局代码,或出现在函数中的整个函数代码),但let仅const适用于它们出现的 块内。 也就是说,var具有函数(或全局)范围,但let具有const块范围。
在相同的上下文中重复var a是无害的,但如果你有let a(or const a),有另一个let aor aconst a或 avar a是语法错误。
var a
let a
const a
这是一个示例,它在该块中的任何代码运行之前立即在其块let中const生效,但在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从块外部访问。
console.log
该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` } })();
在那个例子中,我们定义了一个函数并让它立即执行(()最后的)。
()
以这种方式使用的函数通常称为 作用域函数 。作用域函数中定义的函数可以访问作用域函数中定义的变量,因为它们是对该数据的闭包(请参阅: 我 贫血的小博客上的 闭包并不复杂)。