ES6 總結(二)

摘要:
之前已經對 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的其他知識擴充自己的武器庫。加油!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章