一.var
含義:變量聲明,無論發(fā)生在何處,都在執(zhí)行任何代碼之前進行處理。
注意:
1.聲明變量的作用域限制在其聲明位置的上下文中,而非聲明變量總是全局的
function x() {
y = 1; // 在嚴格模式(strict mode)下會拋出ReferenceError異常。
var z = 2;
}
x();
console.log(y); // 打印"1" 。
console.log(z); // 拋出ReferenceError: z未在x外部聲明。
2. 聲明變量在任何代碼執(zhí)行前創(chuàng)建,而非聲明變量只有在執(zhí)行賦值操作的時候才會被創(chuàng)建。
console.log(a); // 拋出ReferenceError。console.log('still going...'); // 永不執(zhí)行。
var a;
console.log(a); // 打印"undefined"或""(不同瀏覽器實現(xiàn)不同)。
console.log('still going...'); // 打印"still going..."
3. 聲明變量是它所在上下文環(huán)境的不可配置屬性,非聲明變量是可配置的(如非聲明變量可以被刪除)。
var a = 1;b = 2;
delete this.a; // 在嚴格模式(strict mode)下拋出TypeError,其他情況下執(zhí)行失敗并無任何提示。
delete this.b;
console.log(a, b); // 拋出ReferenceError。// 'b'屬性已經被刪除。
建議:建議始終聲明變量,無論它們是在函數(shù)還是全局作用域內。
參考例子:
變量提升:
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í)行前就已經創(chuàng)建了,而賦值操作發(fā)生在創(chuàng)建之后。當"x = y"執(zhí)行時,y已經存在,所以不拋出ReferenceError,并且它的值是'undefined'。所以x被賦予 undefined 值。然后,y被賦予'A'。于是在執(zhí)行完第一行之后,x === undefined && y === 'A'才出現(xiàn)了這樣的結果。
多個變量的初始化
var x = 0;
function f(){
var x = y = 1; // x在函數(shù)內部聲明,y不是!
}
f();
console.log(x, y); // 0, 1// x是全局變量。// y是隱式聲明的全局變量。
隱式全局變量和外部函數(shù)作用域
看起來像是隱式全局作用域的變量也有可能是其外部函數(shù)變量的引用。
var x = 0; // x是全局變量,并且賦值為0。
console.log(typeof z); // undefined,因為z還不存在。
function a() { // 當a被調用時,
var y = 2; // y被聲明成函數(shù)a作用域的變量,然后賦值成2。
console.log(x, y); // 0 2
function b() { // 當b被調用時,
x = 3; // 全局變量x被賦值為3,不生成全局變量。
y = 4; // 已存在的外部函數(shù)的y變量被賦值為4,不生成新的全局變量。 z = 5; // 創(chuàng)建新的全局變量z,并且給z賦值為5。
} // (在嚴格模式下(strict mode)拋出ReferenceError) b(); // 調用b時創(chuàng)建了全局變量z。
console.log(x, y, z); // 3 4 5
}
a(); // 調用a時同時調用了b。
console.log(x, z); // 3 5
console.log(typeof y); // undefined,因為y是a函數(shù)的本地(local)變量。
二.let
含義:允許你聲明一個作用域被限制在塊級中的變量、語句或者表達式。與var關鍵字不同的是,它聲明的變量只能是全局或者整個函數(shù)塊的。
注意:let聲明的變量只在其聲明的塊或子塊中可用,這一點,與var相似。二者之間最主要的區(qū)別在于var聲明的變量的作用域是整個封閉函數(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.簡化內部函數(shù)代碼
當用到內部函數(shù)的時候,let會讓你的代碼更加簡潔。
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);
}
以上示例的工作原理是因為(匿名)內部函數(shù)的五個實例引用了變量i的五個不同實例。注意,如果你將let替換為var,則它將無法正常工作,因為所有內部函數(shù)都將返回相同的i:6的最終值。此外,我們可以通過將創(chuàng)建新元素的代碼移動到每個循環(huán)的作用域來保持循環(huán)更清晰。
每次循環(huán)的i其實都是一個新的變量 注意for循環(huán)的作用域(父子作用域)
在程序或者函數(shù)的頂層,let并不會像var一樣在全局對象上創(chuàng)造一個屬性,比如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
2.模仿私有接口
在處理構造函數(shù)的時候,可以通過let綁定來共享一個或多個私有成員,而不使用閉包:
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ū)的錯誤
在相同的函數(shù)或塊作用域內重新聲明同一個變量會引發(fā)SyntaxError。
if (x) {
let foo;
let foo; // TypeError thrown.
}
在 ECMAScript 2015 中,let綁定不受變量提升的約束,這意味著let聲明不會被提升到當前執(zhí)行上下文的頂部。在塊中的變量初始化之前,引用它將會導致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聲明中你可能會遇到這樣的錯誤,因為它只有一個塊.
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
但是,重要的是要指出嵌套在case子句內的塊將創(chuàng)建一個新的塊作用域的詞法環(huán)境,這不會產生上面顯示的重新聲明錯誤。
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break; }
}
與詞法作用域結合的暫存死區(qū)
由于詞法作用域,表達式(foo + 55)內的標識符“foo”會解析為if塊的foo,而不是覆蓋值為33的foo。在這一行中,if塊的“foo”已經在詞法環(huán)境中創(chuàng)建,但尚未達到(并終止)其初始化(這是語句本身的一部分):它仍處于暫存死區(qū)。
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
這種現(xiàn)象可能會使您陷入以下情況。指令let n of n.a已經在for循環(huán)塊的私有范圍內,因此標識符“n.a”被解析為位于指令本身的第一部分(“l(fā)et n”)中的'n'對象的屬性'a' ,由于尚未達成和終止其聲明,因此仍處于暫存死區(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)建一個常量,其作用域可以是全局或本地聲明的塊。 與var變量不同,全局常量不會變?yōu)榇翱趯ο蟮膶傩浴P枰粋€常數(shù)的初始化器。
注意:
// 注意: 常量在聲明的時候可以使用大小寫,但通常情況下全部用大寫字母。
// 定義常量MY_FAV并賦值7
const MY_FAV = 7;
// 報錯
MY_FAV = 20;
// 輸出 7
console.log("my favorite number is: " + MY_FAV);
// 嘗試重新聲明會報錯
const MY_FAV = 20;
// MY_FAV 保留給上面的常量,這個操作會失敗
var MY_FAV = 20;
// 也會報錯
let MY_FAV = 20;
// 注意塊范圍的性質很重要
if (MY_FAV === 7) {
// 沒問題,并且創(chuàng)建了一個塊作用域變量
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ā)錯誤
var MY_FAV = 20;}
// MY_FAV 依舊為7
console.log("my favorite number is " + MY_FAV);
// 常量要求一個初始值
const FOO;
// SyntaxError: missing = in const declaration
// 常量可以定義成對象
const MY_OBJECT = {"key": "value"};
// 重寫對象和上面一樣會失敗
MY_OBJECT = {"OTHER_KEY": "value"};
// 對象屬性并不在保護的范圍內,下面這個聲明會成功執(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"]
// 但是,將一個新數(shù)組賦給變量會引發(fā)錯誤MY_ARRAY = ['B']