ES6標准之基礎


let和const命令

  1. ES6新增let命令,用於聲明變量,是塊級作用域。

  2. let聲明的變量不會像var聲明的變量發生“變量提升”現象,所以,變量一定要在聲明后使用,不然就會報錯。

  3. 暫時性死區:只要塊級作用域內存在let命令,它所聲明的變量就會“綁定”在這個區域,不再受外部的影響。即在代碼塊內,使用let命令聲明變量之前,這個變量都是不可用的,這在語法上稱為“暫時性死區”。

  4. ES6規定暫時性死區和不存在變量提升,主要是為了減少運行時的錯誤,防止在變量聲明前就使用這個變量,導致意外,這樣的錯誤在ES5中很常見。

  5. let不允許在相同作用域內重復聲明同一個變量。

  6. const命令用來聲明常量,聲明了之后就不能再改變,所以在聲明的時候就必須賦值,這個命令同樣是塊級作用域,同樣存在暫時性死區。

  7. 對於用const聲明的對象,變量名不會指向對象的數據,而是指向對象所在的地址,所以用const聲明的復合類型變量中的數據是可以改變的,這點需要當心!

變量的解構賦值

  1. 解構:ES6允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被成為解構。如下:

    //ES5
    var a = 1;
    var b = 2;
    var c = 3;
    
    //ES6
    var [a, b, c] = [1, 2, 3];
    
    //嵌套
    let [foo, [[bar], baz]] = [1, [[2], 3]];
    foo;//1
    bar;//2
    baz;//3
    
    //如果等號右邊不是可以遍歷的結構,就無法匹配就會報錯。
     
  2. 解構賦值是允許設置默認值的,在ES6內部使用'==='來判斷一個位置是否有值。所以,如果一個數組成員不嚴格等於undefined,默認值是不會生效的。例如:

    [x, y = 'b'] = ['a', undefined];//x='a',y='b'
    [x = 1] = [null];//x=null

     

  3. 默認值可以引用解構賦值的其他變量,但該變量必須已經聲明。例如:

    let [x = 1, y = x] = []; //x=1; y=1
    let [x = 1, y = x] = [2]; //x=2; y=2
    let [x = 1, y = x] = [1, 2]; //x=1; y=2
    let [x = y, y = 1] = []; //報錯,因為x在使用y作為其默認值的時候y還沒有被聲明
     
  4. 對象的解構賦值:

    var {foo, bar} = {foo: "aaa", bar:"bbb"}; foo //"aaa" bar //"bbb"

對象的解構賦值和數組有一個重要的不同:數組的元素是按次序排列的,變量的取值由它的位置來決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。

  1. 對象的解構賦值可以很方便地將現有對象的方法賦值到某個變量。例如:

    let {log, sin, cos} = Math;
    //這樣就可以把取對數、正弦、余弦3個方法賦值到對應的變量上面,用起來很方便;
  2. 字符串也可以解構賦值,因為字符串會被轉換成一個類似數組的對象。例如:

    const [a, b, c, d, e] = 'hello';
    a // 'h'
    b // 'e'
    c // 'l'
    d // 'l'
    e // 'o'
    
    let {length : len} = 'hello';
    len //5
  3. 數值和布爾值的解構賦值:解構賦值時,如果等號右邊是數值或布爾值,則會先轉為對象。

  4. 函數參數也可以解構賦值,例如:

    function add([x,y]){
        return x + y;
    }
    
    add([1, 2]) //3
  5. 變量的解構賦值的用途很多,簡潔易讀:

    • 交換變量的值[x, y] = [y, x]

    • 從函數返回多個值;

    1. example(){
      
      return [1, 2, 3];
      }

       

    2. [a, b, c] = example();

       

    • 函數參數的定義;

      //有序
      function f([x, y, z]){...};
      f([1, 2, 3]);
      
      //無序
      function f({x, y, z}){...};
      f({z:3, y:2, x:1});

       

    • 提取JSON數據,可以快速提取json對象中的數據;

字符串的擴展

ES6加強了對Unicode的支持,並且擴展了字符串對象。

  1. ES5對字符串對象提供了CharAt方法,返回字符串給定位置的字符。但是該方法不能識別碼點大於0xFFFF的字符。於是在ES7中提供了一個at方法,可以識別Unicode編號大於0xFFFF的字符。

  2. includes(),startsWith(),endsWith()方法。JS中只有indexOf方法可以用來確定一個字符串是否包含在另一個字符串中,ES6又提供了三種方法:

    • includes():返回布爾值,表示是否找到了參數字符串;

    • startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部;

    • endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部;

  3. repeat(),repeat方法返回一個新字符串,表示將原字符串重復n次。

  4. padStart(),padEnd():ES7推出了字符串補全長度的功能。如果某個字符串長度未達指定長度,會在頭部或尾部補全。padStart用於頭部補全,padEnd用於尾部補全。如果原字符串的長度大於或等於指定的最小長度,則返回原字符串。例如:

    'x'.padStart(5, 'ab') //'ababx'
    'x'.padStart(4, 'ab') //'abax'
    
    'x'.padEnd(5, 'ab') //'xabab'
    'x'.padEnd(4, 'ab') //'xaba'
    
    'xxx'.padStart(2, 'ab') //'xxx'

正則的擴展

  1. 在ES5中,RegExp構造函數只能接受字符串作為參數var regex = new RegExp("xyz", "i");。在ES6中允許RegExp構造函數接受正則表達式作為參數,這時會返回一個原有正則表達式的拷貝。

  2. ES6新增了使用大括號表示Unicode字符的表示法,這種表示法在正則表達式中必須加上u修飾符才能識別。例如:

    /\u{61}/.test('a'); //false
    /\u{61}/u.test('a'); //true
  3. ES6為正則表達式新增了flags屬性,會返回正則表達式的修飾符。ES5的source屬性會返回表達式的正文。

數值的擴展

  1. ES6提供了二進制和八進制數值的新寫法,分別用前綴0b(或0B)和0o(或0O)來表示。從ES5開始,在嚴格模式中,八進制數值就不再允許使用前綴0表示,ES6進一步明確,要使用0o前綴表示。

  2. Number.isFinite(),Number.isNaN():ES6在Number對象上面新提供了這兩個方法,分別用於檢查Infinite(是否非無窮)和NaN這兩個特殊值。

  3. Number.parseInt(),Number.parseFloat():ES6將全局方法parseInt()和parseFloat()移植到了Number對象上。這樣是為了逐步減少全局性的方法,使語言逐步模塊化。

    //ES5
    parseInt('');
    
    //ES6
    Number.parseInt('');
    
    Number.parseInt === parseInt; //true
  4. Number.isInteger():該方法用來判斷一個值是否為整數。

  5. 新增了一個極小的常量Number.EPSILON,當我們做計算的時候,如果誤差可以小於這個常量,那么就可以認為計算的結果是正確的。

  6. Number.isSafeInteger():JavaScript能夠准確表示的整數范圍在-2{53}到2{53}之間,超出的就不能精確表示了,該函數用來判斷一個數是否落在這個范圍之內。

  7. Math對象的擴展,ES6在Math對象上新增了17個與數學相關的方法:

    • Math.trunc():用於去除小數部分,返回整數部分;

    • Math.sign():用於判斷一個數到底是正數、負數還是0,整數返回1,負數返回-1,0返回0,-0返回-0,其他返回NaN;

    • Math.cbrt():計算一個數的立方根;

    • Math.clz32():返回一個數的32位無符號數有多少個前導0;

    • Math.imul():返回兩個數以32位帶符號整數形式相乘的結果,返回的也是一個帶符號整數,例如:Math.imul(-1, 8); //-8

    • Math.fround():返回一個數的單精度浮點數形式;

    • Math.hypot():返回所有參數平方和的平方根,例如:Math.hypot(3, 4);//5

    • 還有一些和對數運算、三角函數運算、指數運算相關的方法。

數組的擴展

  1. Array.from():將類似數組的對象和可遍歷的對象轉為真正的數組;

  2. Array.of():將一組數值轉換為數組,例如:Array.of(3, 11, 8) //[3,11,8]

  3. fill()方法,使用給定值填充數組,例如:new Array(3).fill(7) //[7,7,7]

  4. 數組實例的entries()、keys()、和values()方法,主要用來遍歷數組,keys()是對鍵名的遍歷,values()是對鍵值的遍歷,entries()是對鍵值對的遍歷;

函數的擴展

  1. ES6之前不能直接為函數的參數指定默認值,所以經常有x = x || "XXX"這樣的寫法,ES6允許為函數的參數設置默認值,就可以這樣寫function test(x, y = "xxx"){};,這樣的設計還有一個好處就是開發人員閱讀別人的代碼一眼就能看出來在調用這個接口哪些參數是可省的。此外,這種寫法還可以和解構賦值結合使用,非常靈活。

  2. 函數的length屬性修改,如果函數中的參數有指定默認值,那么length就不會把這個參數計算進去,例如:(function(a=5){}).length;//0

  3. 作用域問題,如果一個參數的默認值是一個變量,那么這個變量所處的作用域與其他變量的作用域規則是一樣的,先是當前函數的作用域,然后才是全局作用域;

  4. ES6引入了rest參數(形式為"...變量名"),用於獲取函數的多余參數,這樣就不需要使用arguments對象了。rest參數搭配的變量是一個數組,該變量將多余的參數放入其中,例如:

    function add(...values){
        let sum = 0;
        for(var val of values){
            sum += val;
        }
        return sum;
    }
    add(2, 5, 3); //10
    //add函數是一個求和函數,利用rest參數可以向該函數傳入任意數目的參數。
  5. 擴展運算符,三個點(...),作用是把一個數組轉為用逗號隔開的參數序列。例如:console.log(1,...[2,3,4],5);//1 2 3 4 5;

  6. 擴展運算符替代數組的apply方法,擴展運算符可以直接把數組拆開,例如:

    //ES5
    function f(x,y,z){};
    var args = [0,1,2];
    f.apply(null, args);
    
    //ES6
    function f(x,y,z){};
    var args = [0,1,2];
    f(...args);
  7. 擴展運算符提供了數組合並的新方法:

    //ES5
    [1,2].concat(more)
    //ES6
    [1,2, ...more]
  8. 擴展運算符還可以與解構賦值結合;

  9. ES6還寫入了函數的name屬性,可以返回函數名,雖然這個屬性很早就被各個瀏覽器支持了,但是在ES6才正式寫入;

  10. 箭頭函數:ES6允許使用"箭頭"(=>)定義函數,例如:

    var sum = (num1, num2) => num1 + num2;
    //等價於
    var sum = function(num1, num2){
        return num1 + num2;
    }
  11. 使用箭頭函數有幾個注意點:

    • 函數體內的this對象就是定義時所在的對象,而不是使用時所在的對象。在js中this的指向是可以改變的,但是在箭頭函數中this的指向是不變的;

    • 不可以當作構造函數。也就是說,不可以使用new命令;

    • 不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用ES6中的rest參數代替;

    • 不可以使用yield命令,因此箭頭函數不能用作Generator函數;

  12. 函數綁定:在ES6之后的ES7版本中有一個提案是函數綁定運算符(::),雙冒號左邊是一個對象,右邊是一個函數。這個運算符會自動將左邊的對象作為this綁定到右邊的函數上面,例如:foo::bar(...arguments)等價於bar.apply(foo,arguments;)。感覺函數綁定這個設計非常的便捷,不需要在顯式的去綁定一下上下文,期待該提案的通過(目前babel已經支持這個寫法了);

  13. 尾調用:就是指某個函數的最后一步是調用另一個函數;

  14. 尾調用優化:尾調用之所以與其他調用不同,就在於其特殊的調用位置。函數調用的時候會在內存形成一個‘調用記錄’,又稱為‘調用幀’,保存調用位置和內部變量等信息。如果在函數A內部調用函數B,那么在A的調用幀上方還會形成一個B的調用幀。等到B執行結束再返回給A,B的調用幀才消失。如果B的內部調用了C,那么還會產生一個調用幀,以此類推,所有調用幀會形成一個‘調用棧’。然而尾調用是函數的最后一步操作,所以不需要保留外層函數的調用幀,因為調用位置、內部變量等信息都不會再用到了,直接用內層函數的調用幀取代外層函數的即可;

  15. 尾遞歸:函數調用自身稱為遞歸,如果尾調用自身就稱為尾遞歸。遞歸非常耗費內存,因為需要同時保存成千上百個調用幀,很容易stackoverflow。但對於尾遞歸來說,只存在一個調用幀,所以永遠不會發生“棧溢出”錯誤。例如:

     
    //這是一個階乘函數,計算n的階乘,最多需要保存n個調用記錄,復雜度為O(n)。
    function factorial(n){
        if(n === 1) return 1;
        return n * factorial(n - 1);
    }
    //改寫成尾遞歸,只保用一個調用記錄,則復雜度為O(1);
    function factorial(n, totla){
        if(n === 1) return total;
        return factorial(n - 1, n * total);
    }

     

    由此可見‘尾調用優化’對於遞歸操作的意義非常重大,所以一些函數式編程語言將其寫入了語言規格。ES6也是如此,第一次明確規定,所有ECMAScript的實現,都必須部署‘尾調用優化’。這就是說,在ES6中,只要使用尾遞歸,就不會棧溢出,節省內存。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM