一.var
含義:變量聲明,無論發(fā)生在何處顽素,都在執(zhí)行任何代碼之前進(jìn)行處理讯屈。
注意:
1.聲明變量的作用域限制在其聲明位置的上下文中,而非聲明變量總是全局的
function x() {
y = 1; // 在嚴(yán)格模式(strict mode)下會(huì)拋出ReferenceError異常嚎于。
var z = 2;
}
x();
console.log(y); // 打印"1" 。
console.log(z); // 拋出ReferenceError: z未在x外部聲明昼伴。
2. 聲明變量在任何代碼執(zhí)行前創(chuàng)建匾旭,而非聲明變量只有在執(zhí)行賦值操作的時(shí)候才會(huì)被創(chuàng)建。
console.log(a); // 拋出ReferenceError圃郊。console.log('still going...'); // 永不執(zhí)行。
var a;
console.log(a); // 打印"undefined"或""(不同瀏覽器實(shí)現(xiàn)不同)女蜈。
console.log('still going...'); // 打印"still going..."
3. 聲明變量是它所在上下文環(huán)境的不可配置屬性持舆,非聲明變量是可配置的(如非聲明變量可以被刪除)。
var a = 1;b = 2;
delete this.a; // 在嚴(yán)格模式(strict mode)下拋出TypeError伪窖,其他情況下執(zhí)行失敗并無任何提示逸寓。
delete this.b;
console.log(a, b); // 拋出ReferenceError。// 'b'屬性已經(jīng)被刪除覆山。
建議:建議始終聲明變量竹伸,無論它們是在函數(shù)還是全局作用域內(nèi)。
參考例子:
變量提升:
var a = 2;
function foo() {
console.log(a); //undefined
var a = 10;
console.log(a); //10
}
foo();
留意其中的順序:
var x = y, y = 'A';
console.log(x + y); // undefinedA
在這里簇宽,x和y在代碼執(zhí)行前就已經(jīng)創(chuàng)建了勋篓,而賦值操作發(fā)生在創(chuàng)建之后。當(dāng)"x = y"執(zhí)行時(shí)魏割,y已經(jīng)存在譬嚣,所以不拋出ReferenceError,并且它的值是'undefined'钞它。所以x被賦予 undefined 值拜银。然后殊鞭,y被賦予'A'。于是在執(zhí)行完第一行之后尼桶,x === undefined && y === 'A'才出現(xiàn)了這樣的結(jié)果操灿。
多個(gè)變量的初始化
var x = 0;
function f(){
var x = y = 1; // x在函數(shù)內(nèi)部聲明,y不是泵督!
}
f();
console.log(x, y); // 0, 1// x是全局變量趾盐。// y是隱式聲明的全局變量。
隱式全局變量和外部函數(shù)作用域
看起來像是隱式全局作用域的變量也有可能是其外部函數(shù)變量的引用幌蚊。
var x = 0; // x是全局變量谤碳,并且賦值為0。
console.log(typeof z); // undefined溢豆,因?yàn)閦還不存在蜒简。
function a() { // 當(dāng)a被調(diào)用時(shí),
var y = 2; // y被聲明成函數(shù)a作用域的變量漩仙,然后賦值成2搓茬。
console.log(x, y); // 0 2
function b() { // 當(dāng)b被調(diào)用時(shí),
x = 3; // 全局變量x被賦值為3队他,不生成全局變量卷仑。
y = 4; // 已存在的外部函數(shù)的y變量被賦值為4,不生成新的全局變量麸折。 z = 5; // 創(chuàng)建新的全局變量z锡凝,并且給z賦值為5。
} // (在嚴(yán)格模式下(strict mode)拋出ReferenceError) b(); // 調(diào)用b時(shí)創(chuàng)建了全局變量z垢啼。
console.log(x, y, z); // 3 4 5
}
a(); // 調(diào)用a時(shí)同時(shí)調(diào)用了b窜锯。
console.log(x, z); // 3 5
console.log(typeof y); // undefined,因?yàn)閥是a函數(shù)的本地(local)變量芭析。
二.let
含義:允許你聲明一個(gè)作用域被限制在塊級(jí)中的變量锚扎、語句或者表達(dá)式。與var關(guān)鍵字不同的是馁启,它聲明的變量只能是全局或者整個(gè)函數(shù)塊的驾孔。
注意:let聲明的變量只在其聲明的塊或子塊中可用,這一點(diǎn)惯疙,與var相似翠勉。二者之間最主要的區(qū)別在于var聲明的變量的作用域是整個(gè)封閉函數(shù)。
function varTest() {
var x = 1;
if (true) {
var x = 2; // 同樣的變量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的變量
console.log(x); // 2
}
console.log(x); // 1
}
作用:
1.簡(jiǎn)化內(nèi)部函數(shù)代碼
當(dāng)用到內(nèi)部函數(shù)的時(shí)候螟碎,let會(huì)讓你的代碼更加簡(jiǎn)潔眉菱。
var list = document.getElementById('list');
for (let i = 1; i <= 5; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.'); };
list.appendChild(item);
}// to achieve the same effect with 'var'// you have to create a different context// using a closure to preserve the value
for (var i = 1; i <= 5; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
(function(i){
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
})(i);
list.appendChild(item);
}
以上示例的工作原理是因?yàn)椋涿﹥?nèi)部函數(shù)的五個(gè)實(shí)例引用了變量i的五個(gè)不同實(shí)例。注意掉分,如果你將let替換為var俭缓,則它將無法正常工作克伊,因?yàn)樗袃?nèi)部函數(shù)都將返回相同的i:6的最終值。此外华坦,我們可以通過將創(chuàng)建新元素的代碼移動(dòng)到每個(gè)循環(huán)的作用域來保持循環(huán)更清晰愿吹。
每次循環(huán)的i其實(shí)都是一個(gè)新的變量 注意for循環(huán)的作用域(父子作用域)
在程序或者函數(shù)的頂層,let并不會(huì)像var一樣在全局對(duì)象上創(chuàng)造一個(gè)屬性惜姐,比如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
2.模仿私有接口
在處理構(gòu)造函數(shù)的時(shí)候犁跪,可以通過let綁定來共享一個(gè)或多個(gè)私有成員,而不使用閉包:
var Thing;
{
let privateScope = new WeakMap();
let counter = 0;
Thing = function() {
this.someProperty = 'foo';
privateScope.set(this, {
hidden: ++counter,
});
};
Thing.prototype.showPublic = function() {
return this.someProperty;
};
Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}
console.log(typeof privateScope);// "undefined"
var thing = new Thing();
console.log(thing);// Thing {someProperty: "foo"}
thing.showPublic();// "foo"
thing.showPrivate();// 1
暫存死區(qū)的錯(cuò)誤
在相同的函數(shù)或塊作用域內(nèi)重新聲明同一個(gè)變量會(huì)引發(fā)SyntaxError歹袁。
if (x) {
let foo;
let foo; // TypeError thrown.
}
在 ECMAScript 2015 中坷衍,let綁定不受變量提升的約束,這意味著let聲明不會(huì)被提升到當(dāng)前執(zhí)行上下文的頂部条舔。在塊中的變量初始化之前枫耳,引用它將會(huì)導(dǎo)致ReferenceError(而使用 var 聲明變量則恰恰相反,該變量的值是 undefined )孟抗。該變量處于從塊開始到初始化處理的“暫存死區(qū)”迁杨。
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
}
在switch聲明中你可能會(huì)遇到這樣的錯(cuò)誤,因?yàn)樗挥幸粋€(gè)塊.
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
但是凄硼,重要的是要指出嵌套在case子句內(nèi)的塊將創(chuàng)建一個(gè)新的塊作用域的詞法環(huán)境铅协,這不會(huì)產(chǎn)生上面顯示的重新聲明錯(cuò)誤。
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break; }
}
與詞法作用域結(jié)合的暫存死區(qū)
由于詞法作用域摊沉,表達(dá)式(foo + 55)內(nèi)的標(biāo)識(shí)符“foo”會(huì)解析為if塊的foo狐史,而不是覆蓋值為33的foo。在這一行中说墨,if塊的“foo”已經(jīng)在詞法環(huán)境中創(chuàng)建预皇,但尚未達(dá)到(并終止)其初始化(這是語句本身的一部分):它仍處于暫存死區(qū)。
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
這種現(xiàn)象可能會(huì)使您陷入以下情況婉刀。指令let n of n.a已經(jīng)在for循環(huán)塊的私有范圍內(nèi),因此標(biāo)識(shí)符“n.a”被解析為位于指令本身的第一部分(“l(fā)et n”)中的'n'對(duì)象的屬性'a' 序仙,由于尚未達(dá)成和終止其聲明突颊,因此仍處于暫存死區(qū)。
function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]}
for (let n of n.a) { // ReferenceError console.log(n);
}
}
go({a: [1, 2, 3]});
一.const
含義:此聲明創(chuàng)建一個(gè)常量潘悼,其作用域可以是全局或本地聲明的塊律秃。 與var變量不同,全局常量不會(huì)變?yōu)榇翱趯?duì)象的屬性治唤。需要一個(gè)常數(shù)的初始化器棒动。
注意:
// 注意: 常量在聲明的時(shí)候可以使用大小寫,但通常情況下全部用大寫字母宾添。
// 定義常量MY_FAV并賦值7
const MY_FAV = 7;
// 報(bào)錯(cuò)
MY_FAV = 20;
// 輸出 7
console.log("my favorite number is: " + MY_FAV);
// 嘗試重新聲明會(huì)報(bào)錯(cuò)
const MY_FAV = 20;
// MY_FAV 保留給上面的常量船惨,這個(gè)操作會(huì)失敗
var MY_FAV = 20;
// 也會(huì)報(bào)錯(cuò)
let MY_FAV = 20;
// 注意塊范圍的性質(zhì)很重要
if (MY_FAV === 7) {
// 沒問題柜裸,并且創(chuàng)建了一個(gè)塊作用域變量
MY_FAV
// (works equally well with let to declare a block scoped non const variable)
let MY_FAV = 20;
// MY_FAV 現(xiàn)在為 20
console.log('my favorite number is ' + MY_FAV);
// 這被提升到全局上下文并引發(fā)錯(cuò)誤
var MY_FAV = 20;}
// MY_FAV 依舊為7
console.log("my favorite number is " + MY_FAV);
// 常量要求一個(gè)初始值
const FOO;
// SyntaxError: missing = in const declaration
// 常量可以定義成對(duì)象
const MY_OBJECT = {"key": "value"};
// 重寫對(duì)象和上面一樣會(huì)失敗
MY_OBJECT = {"OTHER_KEY": "value"};
// 對(duì)象屬性并不在保護(hù)的范圍內(nèi),下面這個(gè)聲明會(huì)成功執(zhí)行
MY_OBJECT.key = "otherValue";// 也可以用來定義數(shù)組
const MY_ARRAY = [];
// It's possible to push items into the array
// 可以向數(shù)組填充數(shù)據(jù)
MY_ARRAY.push('A');
// ["A"]
// 但是粱锐,將一個(gè)新數(shù)組賦給變量會(huì)引發(fā)錯(cuò)誤MY_ARRAY = ['B']