《ES6標準入門》讀書筆記

《ES6標準入門》讀書筆記

@(StuRep)javascript

圖片發自簡書App

let和const命令

  1. ES6新增let命令,用於聲明變量,是塊級做用域。
  2. let聲明的變量不會像var聲明的變量發生「變量提高」現象,因此,變量必定要在聲明後使用,否則就會報錯。
  3. 暫時性死區:只要塊級做用域內存在let命令,它所聲明的變量就會「綁定」在這個區域,再也不受外部的影響。即在代碼塊內,使用let命令聲明變量以前,這個變量都是不可用的,這在語法上稱爲「暫時性死區」。
  4. ES6規定暫時性死區和不存在變量提高,主要是爲了減小運行時的錯誤,防止在變量聲明前就使用這個變量,致使意外,這樣的錯誤在ES5中很常見。
  5. let不容許在相同做用域內重複聲明同一個變量。
  6. const命令用來聲明常量,聲明瞭以後就不能再改變,因此在聲明的時候就必須賦值,這個命令一樣是塊級做用域,一樣存在暫時性死區。
  7. 對於用const聲明的對象,變量名不會指向對象的數據,而是指向對象所在的地址,因此用const聲明的複合類型變量中的數據是能夠改變的,這點須要小心!

變量的解構賦值

  1. 解構:ES6容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被成爲解構。以下:java

    //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. 對象的解構賦值:json

    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. 函數參數也能夠解構賦值,例如:babel

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

    • 交換變量的值[x, y] = [y, x]
    • 從函數返回多個值;
    function example(){
        return [1, 2, 3];
    }
    var [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中,只要使用尾遞歸,就不會棧溢出,節省內存。

(未完待續......)

相關文章
相關標籤/搜索