摘要:
之前已經對 ES6有了一個詳細的介紹,作爲一個前端 user,想要打好基礎就必須瞭解 ES6,
這邊文章主要是關於 :let和 const、解構賦值、symbol的一些總結,看完這篇文章相信你會對這些知識有一個清晰的認識。
ES6 let與 const
ES6新增加了兩個重要的 JavaScript 關鍵字:let 和 const。
let聲明的變量只在 let 命令所在的代碼塊內有效。
const 聲明一個只讀的常量,一旦聲明,常量的值就不能改變了。
1、let 命令
基本用法:
{
let a = 0;
a //0
}
a // 報錯 referenceError: a is not defined
代碼塊內有效
let 是在代碼塊內有效,var 是在全局範圍內有效:
{
let a = 0;
var b = 1;
}
a // referenceError: a is not defined
b // 1
不能重複聲明
let 只能聲明一次 var 可以聲明多次:
let a = 1;
let a = 2;
var b = 3;
var b = 4;
a // Identifier 'a' has already been declared
b // 4
針對於 for 循環計數器,let 很適合
for (var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
})
}
//輸出十個10
for (let j = 0; j < 10; j++){
setTimeout(function(){
console.log(j)
})
}
//輸出0123456789
變量 i 是用 var 聲明的。在全局範圍內有效,所以全局中只有一個變量 i,每次循環時,setTimeout 定時器裏面的 i 指的就是全局變量 i,而循環裏的十個 setTimeout 是在循環結束後才執行,所以此時的 i 都是10。
變量 j 是用 let 聲明的,當前的 j只在本輪循環中有效,每次循環的 j其實就是一個新的變量,所以 setTimeoout 定時器裏面的 j 其實是不同的變量,即輸出0123456789(若每次循環的變量 j 都是重新聲明的,如何知道前面的值?這是因爲 Javascript 引擎內部會記住前一個循環的值)。
不存在變量提升
let 不存在變量提升,var 會變量提升:
console.log(a); //referenceError:a is not defined
let a = 'apple';
console.log(b); //undefined
var b = 'banana';
變量 b 用 var 聲明聲明存在變量提升,所以當腳本開始運行的時候,已經存在了,但是還沒有賦值,所以會輸出 undefined。
變量 a用 let 聲明不存在變量提升,在聲明變量a 之前,a不存在,所以會報錯。
2、const 命令
const聲明一個只讀變量,聲明之後不允許改變。意味着,一旦聲明必須初始化,否則會報錯。
基本用法:
const PI = "3.1415926";
PI //3.1415926
const MY_AGE; //SyntaxError: Missing initializer in const declaration
暫時性死區:
var PI = 'a'
if(true){
console.log(PI); //referenceError: PI is not defined
const PI = "3.1415926";
}
ES6明確規定,代碼塊內如果存在 let或者 const,代碼塊會對這些命令聲明的變量從塊的開始就形成一個封閉作用域。代碼塊內,在聲明變量 PI 之前使用它會報錯。
注意要點
const 如何做到變量在聲明初始化之後不允許改變的?其實 const 保證的不是變量的值不變,而是保證變量指向的內存地址所保存的數據不允許改動。此時,你可能已經想到,簡單類型和複用類型保存值的方式是不同的。對於簡單類型(數值number,字符串string,布爾值 boolean),值就保存在變量指向的那個內存地址,因此 const 聲明的簡單類型變量等同於常量。而複雜類型(對象 object,數組 array,函數function),變量指向的內存地址其實是保存了一個指向實際數值的指針,所以 const 只能保證指針是固定的,至於指針指向的數據結構變不變就無法控制了,所以使用 const 聲明覆雜類型對象時要慎重。
ES6解構賦值
解構賦值是對賦值運算符的擴展。
他是一種針對數組或者對象進行模式匹配,然後對其中的變量進行賦值。
在代碼書寫上簡潔且易讀,語義更加清晰明瞭;也方便了複雜對象中數據字段獲取。
1、解構模型
在解構中,有下面兩部分參與:
·解構的源,解構賦值表達式的右邊部分。
·解構的目標,解構複製表達式的左邊部分。
2、數組模型的解構
基本:
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
可嵌套:
let [a, [ [b], c ] ] = [ 1, [ [2], 3 ] ];
//a = 1
//b = 2
//c = 3
可忽略:
let [ a, , b ] = [ 1, 2, 3 ];
// a = 1
// b = 3
不完全解構
let [ a = 1, b ] = []; //a = 1, b = undefined
剩餘運算符
let [ a, ...b ] = [ 1, 2, 3];
// a = 1
// b = [ 2, 3]
字符串等
在數組的解構中,解構的目標若爲可遍歷對象,皆可進行解構賦值。可遍歷對象即實現 lterator接口的數據。
let [ a, b, c, d, e ] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'e'
解構默認值
let [ a = 2 ] = [ undefined ]; // a = 2
當解構模式有匹配結果,且匹配結果是 undefined,會觸發默認值作爲返回結果。
let [ a = 3, b = a ] = []; // a = 3 b = 3
let [ a = 3, b = a ] = [ 1 ]; // a =1 b = 1
let [ a = 3, b = a ] = [ 1, 2 ]; // a = 1 b = 2
·a與 b匹配結果爲 undefined,觸發默認值: a = 3; b = a = 3
·a 正常解構賦值, 匹配結果: a = 1, b 匹配結果 undefined,觸發默認值:b = a = 1
·a 與b 正常解析,匹配結果:a = 1, b = 2
3、對象模型的解構
基本:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
//foo = 'aaa'
//bar = 'bbb'
let { baz: foo } = { baz : 'ddd' };
//foo = 'ddd'
可嵌套可忽略
let obj = { p: [ 'hello', {y: 'world'}] };
let { p: [x, { y }] } = obj;
//x = 'hello'
//y = 'world'
let obj = { p: ['hello', {y: 'world'}] };
let { p: [x, {}] } = obj;
//x = 'hello'
不完全解構
let obj = { p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
//x = undefined
//y = 'world'
剩餘運算符
let { a, b, ...reset} = {a: 10, b: 20, c: 30, d: 40};
//a = 10
//b = 20
// reset = {c: 30, d: 40}
解構默認值
let {a = 10, b = 5} = { a: 3};
//a = 3; b = 5;
let { a: aa = 10, b: bb =5 } = { a: 3 };
// aa = 3; bb = 5;
ES6 Symbol
ES6引入了一種新的原始數據類型 Symbol,表示獨一無二的值,最大的用法是用來定義對象的唯一屬性名。
ES6數據類型除了 Number、String、Boolean、Object、null和 undefined,還新增了 Symbol。
基本用法
Symbol 函數棧不能用 new 命令,因爲 Symbol 是原始數據類型,不是對象。可以接受一耳光字符串作爲參數,爲新創建的Symbol 提供描述,用來顯示在控制檯或者作爲字符串的時候使用,便於區別。
let sy = Symbol("kk");
console.log(sy); //Symbol(kk)
typeof(sy); //"symbol"
//相同參數 Symbol()返回的值不相等
let sy1 = Symbol("kk");
sy === sy1; //false
使用場景
作爲屬性名
用法
由於每一個 Symbol 的值都是不相等的,所以 Symbol 作爲對象的屬性名,可以保證屬性不重名。
let sy = Symbol("key1");
//寫法1
let syObject = {};
syObject[sy] = 'kk';
console.log(syObject); //{Symbol(key): "kk"}
//寫法2
let syObject = {
[ sy ]: "kk"
};
console.log(syObject); // {Symbol(key1): "kk"}
//寫法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject); //{Symbol(key1): "kk"}
Symbol 作爲對象屬性名時不能用".“運算符,要用方括號。因爲”."運算符後面是字符串,所以取到的是字符串 sy 屬性,而不是 Symbol 值sy屬性。
let syObject = {};
syObject[ sy ] = "kk";
syObject[ sy ]; //"kk"
syObject.sy; //undefined
注意點
Symbol 值作爲屬性名時,該屬性是共有屬性而不是私有屬性,可以在類的外部訪問。但是不會出現在 for…in、for…of的循環中,也不會被 Object.keys()、Object.getOwnPropertyNames()返回。如果要讀取到一個對象的 Symbol 屬性,可以通過 Object.getOwnPropertySymbols()和 Reflect.ownKeys()取到。
let syObject = {};
syObject[ sy ] = "kk";
console.log(syObject);
for(let i in syObject) {
console.log(i);
}//無輸出
Object.keys(syObject); //[]
Object.getOwnPropertySymbols(syObject); //[Symbol(key1)]
Reflect.ownKeys(syObject); //[Symbol(key1)]
定義常量
在 ES5使用字符串表示常量。例如:
const COLOR_RED = 'red';
const COLOR_YELLOW = "yellow";
但是字符串不能保證常量是獨特的,這會引起一些問題:
const COLOR_RED = "red";
const COLOR_YELLOW = "yellow";
const COLOR_BULE = "blue";
const MY_BULE = "bule";
function getConstantName(color) {
switch (color) {
case COLOR-RED :
return "COLOR_RED";
case COLOR_YELLOW:
return "COLOR_YELLOW";
case COLOR_BLUE:
return "COLOR_BLUE";
case MY_BLUE:
return "MY_BLUE";
default:
throw new Exception('Can't find this color');
}
}
但是使用 Symbol 定義常量,這樣就可以保證這一組常量的值都不想等。用 Symbol 來修改上面的例子。
const COLOR_RED = Symbol("red");
const COLOR_YELLOW = Symbol("yellow");
const COLOR_BULE = Symbol("blue");
function getConstantName(color) {
switch (color) {
case COLOR-RED :
return "COLOR_RED";
case COLOR_YELLOW:
return "COLOR_YELLOW";
case COLOR_BLUE:
return "COLOR_BLUE";
case MY_BLUE:
return "MY_BLUE";
default:
throw new Exception('Can't find this color');
}
}
Symbol 的值是唯一的,所以不會出現相同值得常量,即可以保證 switch 按照代碼預想的方式執行。
Symbol.for()
Symbol.for()類似單例模式,首先會在全局搜索被登記的Symbol 中是否有該字符串參數作爲名稱的 Symbol 值,如果有即返回該 Symbol 值,若沒有則新建並返回一個以該字符串參數爲名稱的 Symbol 值,並登記在全局環境中供搜索。
let yellow = Symbol("yellow");
let yellow1 = Symbol.for("yellow");
yellow === yellow1; //false
let yellow2 = Symbol.for("yellow");
yellow1 === yellow2; //true
Symbol.keyFor()
Symbol.keyFor()返回一個已登記的 Symbol 類型值得 key,用來檢測該字符串參數作爲名稱的 Symbol 值是否已被登記。
let yellow1 = Symbol.for("yellow");
Symbol.keyFor(yellow1); // 'yellow'
總結
這裏對 let、const、解構賦值、Symbol 有了一個大致的總結,明白了這些知識的概念,當然 ES6的知識不會只有這麼一點,接下來還會總結 ES6的其他知識擴充自己的武器庫。加油!