ES6內容

iterator

遍歷器iterator

makeIterator是個遍歷器,生成遍歷器對象itnode

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
    var nextIndex = 0;
    return {
        next: function() {
            return nextIndex < array.length ?
            {value: array[nextIndex++], done: false} :
            {value: undefined, done: true};
        }
    };
}

iterable接口

對象具備Symbol.iterator方法就是部署了iterable接口,但若是Symbol.iterator返回的不是遍歷對象,會報錯。下面對象部署了iterable接口git

var obj = {
    value : 1,
    [Symbol.iterator](){
        let me = this
        return {
            next(){
                return {
                    value: me.value++,
                    done: me.value >= 10
                }
            }
        }
    }
}

iterable接口爲數據結構提供統一的數據訪問機制,具備線性訪問特色。es6

原生部署的有:數組、相似數組對象、Set和Map,數組、Set、Map的entries、keys、values方法的返回。
相似數組對象如 arguments對象 DOM NodeList對象;github

對象部署Iterator接口:ajax

function* entries(obj){
     for(let key of Object.keys(obj)){
          yield [key, obj[key]]
     }
}

部署了iterable接口的數據結構使用場景

解構賦值、擴展運算符、yield *、for…of、Array.from、Map()、Set() Promise.all Promise.tracechrome

for…of 對比for…in

  • for…in編程

    獲取鍵名,無序的,循環對象全部可枚舉的屬性,做用數組時,返回的key是字符串json

  • for…of數組

    of後面跟遍歷器對象,或者部署了遍歷器接口的數據類型。返回有序元素,相對與forEach能夠break、continuepromise

    若是跟makeIterator,會報錯

    for(let v of [1,2,3]){console.log(v)}
      for(let v of [1,2,3][Symbol.iterator]()){console.log(v)}//兩結果同樣

其它

遍歷器對象的return方法

通常用在for...of循環中提早退出break時或者throw,會執行定義在遍歷器中return方法,return方法必須返回一個對象

'use strict';
/**
 * node v6.9.0 chrome52下執行return
 * @type {{cur: number}}
 */
var obj = {
  cur: 1,
  next() {
    return {
      value: this.cur++,
      done: false
    }
  },
  return() {
    this.cur = 0
    console.log('execute iterator\'s method of return')
    return {
      done: true
    }
  },
  [Symbol.iterator]() { // 沒有會報錯
    return this;
  }
};
for (let v of obj[Symbol.iterator]()) {
  //throw new Error(); 也會執行return
  if (v > 10) {
    break;
  }
  if (v == 8) {
    continue;
  }
  console.log(v);
}

generator函數

  • generator函數生成了多個返回值,每次執行返回一個狀態。
  • generator函數做爲異步編碼的解決方案,提供函數執行時暫停功能。

generator函數相關知識點

  • yield右邊的表達式是延遲執行的
  • 表達式中yield要用()包起來,可是函數參數,表達式右邊的例外
  • generator函數執行後,返回遍歷器對象g,該對象具備Symbol.iterator方法,執行後返回自身。
  • generator函數內部的return,執行到return,返回value等於return,done等於true
  • g.next(data) data參數會傳到上一個yield停留地方,做爲其返回值
  • g.return(data) 返回{value:data,done:true},nodeJs6.2.2支持。

    /**
       * return nodeJs暫不支持,調用會報錯,nodeJs6.2.2支持
       */
      function* gen(){
          yield 1;
          try{
              yield 11;
              yield 22;
              yield 33;
          }finally{
              console.log('a')
              yield 21;
              yield 22;
          }
          yield 2;
      }
      let itr = gen()
      console.log(itr.next()) // { value: 1, done: false }
      console.log(itr.return(31)) // { value: 31, done: true }
      console.log(itr.next()) // { value: undefined, done: true }
      console.log(itr.next()) // { value: undefined, done: true }
      console.log(itr.next()) // { value: undefined, done: true }

generator拋錯

g.throw()
  1. 遍歷器對象能夠執行throw,generator函數內部有try...catch,再被catch,直到運行結束或者執行時遇到下一個next。若是沒被捕獲,好比連續throw兩次,會拋到generator外面。

    /**
      * 一開始沒執行next,就執行throw,內部沒法捕獲。
      */
     function* gen(){
         try{
             console.log('start')
             yield 1;
         }catch(e){
             console.log('內部捕獲',e)
         }
         yield 2;
     }
     var g = gen();
     try{
         //g.throw('a');
         console.log(g.next())
         console.log(g.throw('b'))
     }catch(e){
         console.log('外部捕獲',e)
     }
    
    
     /**
      * 遍歷器對象連續兩次throw
      */
    
     function* errCnt() {
         try {
             yield 5 // { value: 5, done: false }
         } catch (e) {
             console.log('內部捕獲', e)
             //throw new Error('cry')
         }
         yield 6
         yield 7
     }
     var itr = errCnt()
     console.log(itr.next())
     console.log(itr.throw('a')) // { value: 6, done: false }
     try {
         console.log(itr.throw('a'))
     } catch (e) {
         console.log('外部捕獲', e)
         console.log(itr.next()) // { value: undefined, done: true }
     }
  2. 若是錯誤有沒被捕獲,程序中斷執行。
  3. 遍歷器對象throw,generator內部捕獲後,會順帶執行下一個next。
  4. 沒有執行過next,就執行throw,內部沒法捕獲。
  5. Generator函數體內報錯,外部捕獲後,下次還執行next,done爲true,value undefined

    'use strict';
    
     function* foo() {
       let x = yield 3;
       let y = x.toUpperCase();
       yield y;
       yield 5;
     }
    
     var it = foo();
    
     console.log(it.next()); // { value:3, done:false }
    
     try {
       console.log(it.next(42));
     } catch (err) {
       console.log(err);
       console.log(it.next()); // {value: undefined, done: true}
     }
  6. Generator的遍歷器對象拋錯後,內部沒捕獲,外部捕獲,下次執行next,同上

var a = yield * AA

AA必須是部署了遍歷器接口的數據結構,A若是是Generator生成的,且Generator有return,會將值賦值到a。

function* gen1(){
    yield 1;
    yield 2;
    return 3;
}
function* gen2(gen){
    yield 11;
    let rtn = yield* gen();
    console.log('rtn: ' + rtn)
    yield 22;
}
let itr1 = gen1()
console.log(itr1.next()) // { value: 1, done: false }
console.log(itr1.next()) // { value: 2, done: false }
console.log(itr1.next()) // { value: 3, done: true }

let itr2 = gen2(gen1)
console.log(itr2.next()) // { value: 11, done: false }
console.log(itr2.next()) // { value: 1, done: false }
console.log(itr2.next()) // { value: 2, done: false }
                         // rtn: 3
console.log(itr2.next()) // { value: 22, done: false }
console.log(itr2.next()) // { value: undefined, done: true }
for(let v of gen2(gen1)){
    console.log(v)
}

AA若是直接是Generator,會報錯;for...of若是直接是Generator,也會報錯。

this

function* gen(){
     yield 1;
     yield 2;
     yield this.name = ’sprying'
}  
gen.prototype.sayHi = () =>console.log(‘hi')
const g = gen()
g.next()
g.next()
g.next()

g.sayHi()
g.name // null

若是上面const g = gen()換成

const g = gen.apply(gen.prototype)

這時候g.name就有值
注意new gen會報錯

Generator函數的原型時Generator.prototype(假設是ownProto);
ownProto原型是sharedProto,sharedProto.hasOwnProperty("next")
sharedProto的原型是iterProto,iterProto.hasOwnProperty(Symbol.iterator)

generator函數兩大用途

做爲異步編程的解決方案

函數遇到異步操做時,yield暫停,異步回調觸發時,再繼續執行。具體實現流程以下:

  1. 當執行到yield時暫停,執行yield右邊表達式,後將結果和done狀態返回調用者。
  2. 調用者根據結果判斷是否繼續執行,或者異步時等到何時執行,執行的時候傳回什麼給generator內部。
  3. 何時執行,傳回什麼,是經過異步編程協議來規範,好比promise、thinkify。
    • promise

      yield返回結果是promise對象,異步響應時,觸發then,即

      // 業務代碼
        function * gen(){
            yield new Promise((resolve, reject) => {
                setTimeout(()=>{
                    resolve({ok: true})
                }, 500)
            })
        }
        // 執行器背後的核心處理邏輯,執行next後,value是promise
        promise.then(function(data){
            g.next(data)
        })
    • thunkify

      yield返回參數是函數,傳入回調,執行這個函數。異步響應時,執行回調,回調的參數是data,即。

      // 業務代碼
        function * gen(){
            yield function(callback){
                setTimeout(()=>{
                    callback({ok: true})
                }, 500)
            }
        }
      
        // 執行器背後的核心處理邏輯
        hook((data) = >{
            g.next(data)
        })

      假設下面場景

      fs.readFile('/etc/passwd', (err, data) => {
          if (err) throw err;
          console.log(data);
        });
      
        yield function(callback){   
            fs.readFile('/etc/passwd', (err, data) => {
                if (err) throw err;
                callback(data)
            });
        }

      每次都這樣寫,是否是很麻煩?咱們能夠封裝個函數

      function thunkify(callback){
            return function(...args){
                return function(hook){
                    callback(...[...args, hook])
                }
            }
        }
      
        // 下面就簡潔了不少
        const readFile = thunkify(fs.readFile)
        function * gen(){
            yield   readFile(filename)
        }

生成具備iterable接口數據結構

function* gen(){
    yield 1; 
    yield 2; 
    yield 3; 
    return 4;
}
for(let v of gen()){
    console.log(v)
}//1,2,3

promise

promise知識點

  • 實例化Promise時,傳入的函數當即執行,then在當前同步執行完時執行
  • resolve能夠傳入下一個promise實例

    /**
       * 注意執行順序
       */
      setTimeout(()=>console.log(1),0)
      new Promise(function (resolve, reject) {
          console.log(2);
          x/2;
          resolve();
      }).then(function () {
          console.log(3)
          //throw new Error('err')
      },function(err){
          console.log(err)
      });
      console.log(4)

promise錯誤

  • 捕獲錯誤,能夠在then第二參數回調中,但建議使用catch。
  • 運行中出現錯誤會觸發報錯
  • 狀態已經resolve再throw錯誤,是無效的,若是前面狀況是setTimeout再throw,錯誤會拋到外面。
  • catch後再then,若是then中再發生錯誤是沒法被前面catch捕獲的。
  • catch中報錯,後面也沒catch,致使沒法捕獲,也不會傳遞到外層。
  • 錯誤發生後,無論後面有幾個catch,只會被第一個catch捕獲執行,後面then還能夠繼續執行
  • 錯誤沒被捕獲,觸發下面

    process.on('unhandledRejection', function (err, p) {})
    
    
      /**
       * reject未被catch,會被傳遞到unhandledRejection
       * Promise內部throw沒被catch,會被傳遞到unhandledRejection,其它不作任何處理
       * throw後有catch,還有then,可是then應該是catch生成的默認resolve的promise
       */
      new Promise((resolve, reject) => {
          //reject(new Error('err'))
          //setTimeout(() => {
              throw new Error('err-1')
          //}, 0)
          //resolve('done')
      })
          .catch(err => console.log('err: ', err))
          .then((data) => {
              console.log('then: ' + data)
          })
      process.on('unhandledRejection', function (err, p) {
          //console.error(err.stack)
      });
    
      var someAsyncThing = function () {
          return new Promise(function (resolve, reject) {
              // 下面一行會報錯,由於x沒有聲明
              resolve(x + 2);
          });
      };
    
      // 注意執行時間
      ///*
      someAsyncThing()
          .catch(function (error) {
              console.log('oh no', error);
          })
          .then(function () {
              console.log('carry on');
          });
          //*/

Promise.all

參數是具備Iterator接口的對象,若是都是fulfilled,觸發的then的參數回調的參數是數組。若是首先一個出來rejected,錯誤捕獲的回調參數是首先出現錯誤那個。Promise.all對應的各個值若是不是Promise,調用Promise.resolve

Promise.race

誰先改變狀態,結果和回調的參數就聽誰

Promise.resolve

var jsPromise = Promise.resolve($.ajax('/whatever.json'));

參數是個thenable對象,當即執行thenable對象的then

參數是其它值,好比字符串

參數是空的

/**
 * Created by yingchun.fyc@alibaba-inc.com on 16/7/13.
 */
let thenable = {
    then(resolve, reject){
        resolve('thenable')
    }
}
Promise.resolve(thenable).then(res => console.log(res))


var p = Promise.resolve('hi')
p.then(res => console.log(res))


// 輸出
// hi
// thenable

Promise.reject

參數同上

最新異步編程方式

Generator+(Promise/thunk)

generator函數容許執行暫停。當遇到yield時暫停,Js代碼能夠控制繼續執行,好比控制yield後面的異步響應後,繼續執行。暫停、和繼續執行是咱們寫的Js代碼控制,也就是,實現個執行器,讓這些操做自動執行,就能夠實現同步方式寫異步代碼。

執行器是個函數,返回promise。約定yield後面跟promise或者thunk函數。執行器執行到g.next(),返回obj,obj.value裏的異步響應後,若是obj.done爲false,繼續執行,將響應的值傳給g.next(data),如此循環。若是何時g.next,返回的done是true,那麼等待value異步響應後,resolve響應的值,結束循環。

若是g.next時,報錯,首先g.throw給generator處理,處理完繼續next;處理失敗,直接結束。若是異步響應出錯,一樣處理。thunk函數,異步響應時執行callback,規定第一參數傳錯誤信息。

要並行處理異步時,yield後面跟數組、對象,執行器Promise.all下處理。

若是Generator函數,再套了一個Generator,放在yield後面。執行器裏就再調一次執行器。

上面就是co的實現原理。

如今咱們反思這樣一個問題,同步方式寫異步代碼,遇到異步時,程序等待異步響應後,再繼續執行,那麼nodeJs異步處理優點就沒了嗎?一開始,我也是這麼認爲的,可是co執行Generator函數時,雖然內部yield暫停了,可是整個Generator並無所以卡住,仍是會繼續執行co後續邏輯。因此nodeJs異步處理的優點還在。

async

async屬於ES7,不過引入babel的transform-async-to-generator就能夠轉碼使用。await在ES6是保留字,ES5中使用它是合法。

普通函數中使用await會報錯。

Async函數有多種使用形式。

// 函數聲明
async function foo() {}

// 函數表達式
const foo = async function () {};

// 對象的方法
let obj = { async foo() {} };

// 箭頭函數
const foo = async () => {};

async、await組合跟執行器co很像,只是它再也不須要執行器了,像通常函數調用,返回的仍然是Promise。await相比co的yield,後面能夠是基本數據類型。

至於錯誤處理機制,實際上跟co結果同樣。不想結束,也是須要try...catch。或者promise後加catch。

至於併發執行,先都生成Promise實例,而後await。或者Promise.all([asyncMethod1(), asyncMethod2()])

/**
 * Created by yingchun.fyc@alibaba-inc.com on 16/9/12.
 */
async function f() {
  try {
    await Promise.reject('出錯了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
  .then(v => console.log(v))

async function dbFuc(fn) {
  let docs = [1,2,3];
  let promises = docs.map((doc) => fn(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的寫法
// 兩個運行實際差很少,由於都是先生成了promise,再調用await。await、yield,參數默認值都是延遲執行的。

async function dbFuc1(fn) {
  let docs = [4,5,6];
  let promises = docs.map((doc) => fn(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}
var startTime = (new Date()).getTime()
dbFuc1((data) => {
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      resolve(data)
    }, 1000)
  })
}).then(() =>{
  console.log((new Date().getTime() - startTime))
})

class

  • 關鍵字class定義類,方法之間沒有,,方法名能夠是變量[defineVar],定義的class只能new,不然報錯。方法名不可枚舉。構造函數在constructor方法中定義

  • 可使用Object.assign新增方法。

  • class 不存在變量提高

  • class表達式

    let myClass = class Me {} //Me.name只能內部調用,Me也只能內部用
      myClass.name // Me
  • 實例的__proto__,指向原型對象

  • 私有方法

    使用Symbol定義方法名

    const bar = Symbol(「bar")

繼承extends

繼承重寫constructor,要在constructor中先調用super(),不然會報錯。

super在對象方法中均可以使用,可是奇怪的是,下面第一個正常,第二個會報錯。

var obj = {
    toString() {
        return "MyObject: " + super.toString();
    }
};
var obj = {
    toString: function() {
        return "MyObject: " + super.toString();
    }
};

能夠繼承原生的構造函數,可是繼承Object時,沒法向父類的構造函數傳入參數。

相比es5的繼承不能繼承原生的構造函數,即便es5繼承了,也不具有原生的能力。

Object.getPrototypeOf獲取類的父類

Object.getPrototypeOf(B) === A
Object.setPrototypeOf
Object.setPrototypeOf = function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
}

其它

方法名前可加set/get,而且是定義在descriptor上,Object.getOwnPropertyDescriptor

方法名前可加static

es7才支持屬性名直接在類裏定義

關於class具體使用例子,這裏就不貼出來了。

解構賦值destructuring

數組的解構賦值

支持嵌套,等號右邊是可遍歷(iterator)的結構

等號兩邊能夠不徹底匹配,或左邊小,或右邊沒相應值(這時undefined)

支持默認值,默認值若是是表達式,表達式是惰性求值的

let [x=y,y=1] = [1,2]

let [a=b,b=1] = []// Uncaught ReferenceError: b is not defined(…)

var [a, , [b], c] = [5, null, [6]]; 
var [a, , [b], c] = [5, undefined, [6]];
var [a, , [b], c] = [5, , [6]];// a = 5; b = 6; c = undefined

var [a, b, c] = "ab"; // a = 'a'; b = 'b'; c = undefined

var [c] = "𠮷嗰"; // c = "𠮷"

var [a,] = [1]; // a = 1

對象的解構賦值

變量名和屬性名不一致,是經過模式對應起來

var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // Uncaught ReferenceError: foo is not defined(…)

聲明語句、賦值語句

let foo;
let {foo} = {foo: 1};// Uncaught SyntaxError: Identifier 'foo' has already been declared
({foo} = {foo: 1})// 不報錯

嵌套

var node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};

var { loc: { start: { line }} } = node;
line // 1
loc  // Uncaught ReferenceError: loc is not defined(…)
start // error: start is undefined

let obj = {};
let arr = [];

({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });

obj // {prop:123}
arr // [true]

默認值

var {x:y =3} = {x:5}
// y 5

賦值時嚴格的等於undefined

解構失敗

var {foo} = {bar: 'baz'};
foo // undefined

其它

var {foo: {bar}} = {baz: 'baz'};
Uncaught TypeError: Cannot match against 'undefined' or 'null'.(…)

var qux = "corge";
var { [qux]: grault } = { corge: "garply" } // grault = "garply"

解構賦值容許,等號左邊的模式之中,不存在任何變量名

({} = [true, false]);

能夠將對象的方法,解構賦值給變量

let { log, sin, cos } = Math;

字符串、數值、布爾值的解構賦值

let {length : len} = 'hello';
let {toString: s} = 123;
s === Number.prototype.toString // true

解構賦值的規則是,只要右邊的值不是對象,先轉爲對象。因爲undefined和null沒法轉爲對象,因此解構賦值時,會報錯

函數參數的解構賦值

function move({x = 0, y = 0} = {}) {
  return [x, y];
}

函數參數的默認值

[1, undefined, 3].map((x = 'yes') => x);

可使用()場景

賦值語句,非模式部分

[(b)] = [3]; // 正確
({ p: (d) } = {}); // 正確
[(parseInt.prop)] = [3]; // 正確
var a, b;
({a,b} = {a:1,b:2});
try {
  eval("({a,b}) = {a:3,b:4};");//報錯
}
catch(e) {
  return a === 1 && b === 2;
}


var [a,b] = [5,6], {c,d} = {c:7,d:8};

rest

var [c, ...d] = [6]; // c = 6; d.length=0

用途

[x, y] = [y, x];

函數返回多個值

var [a, b, c] = example();

函數參數的默認值

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff
};

遍歷Map結構

var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}

輸入模塊的指定方法

const { SourceMapConsumer, SourceNode } = require("source-map");

函數的擴展

函數參數的默認值

函數參數若是是對象的解構賦值,執行的時候對象的解構賦值要有值,不論是函數參數默認值或者是傳入的值

函數參數的默認值是個表達式,表達式會延遲執行

函數參數默認值的做用域,是函數的做用域

var x = 1;
function foo(x, y = function() { x = 2; }) {
  var x = 3;
  y();
  console.log(x);
}

foo() // 3

感受比較奇怪,按理來講,y執行時的做用域,就是定義時的做用域,參數中的做用域都是函數範圍內做用域。因此參數中x就應該與下面一致。實際上最後輸出的x爲3

把var換成let,在chrome 52下就報錯了

函數的參數默認值位置

建議位於後面幾個參數,若是有默認值的參數,後面還有要輸入的參數,執行的時候,foo(undefined, 1)

函數的length

除去有默認值參數,以及後面的全部參數

function testfn1(a = 1, x, h, y =2){}
testfn1.length // 0

函數必填參數

function throwIfMissing(){
    throw new Error('Missing parameter')
}
function foo(mustBeProvided = throwIfMissing()){
    return mustBeProvided
}
foo()

rest參數

對應的變量是個數組,後面不能再跟其它參數,function的length是除了rest參數的函數長度;
arguments.length仍是按之前方式

(function (foo, ...args) {
  foo = "qux";
  // The arguments object is not mapped to the
  // parameters, even outside of strict mode.
  return arguments.length === 3
    && arguments[0] === "foo"
    && arguments[1] === "bar"
    && arguments[2] === "baz";
}("foo", "bar", "baz"))

擴展運算符....

後跟部署iterale接口的數據結構,將數據結構變成逗號相隔的參數序列

console.log(1, ...[2, 3, 4], 5)

function add(x, y) {
  return x + y;
}

var numbers = [4, 38];
add(...numbers) // 42

let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};
[...arrayLike]
Uncaught TypeError: arrayLike[Symbol.iterator] is not a function(…)

Array.from(arrayLike)
["a", "b", "c"]

函數的name屬性

函數表達式定義方式中,相比es5,es6中函數的name是被賦值的名字。
bind的函數,返回bound開頭

let fn1 = function(){}
let fn2 = fn1
n2.name //fn1

(new Function).name //anonymous
fn1.bind({}).name //bound fn1
(function(){}).name //
(function(){}).bind({}).name //"bound "

箭頭函數

let arrowFn1 = ({x,y}) => [x,y]
let arrowFn2 = (...arr) => [...arr]

不具備this,不能當作構造函數,沒有本身的arguments。
this指向定義時所處的this,使用bind無效

函數綁定::

es7內容,前面是綁定的對象,後面是函數,babel轉碼器已經支持

ES6的對象

簡寫

直接量寫法中,容許屬性值省略,方法簡寫,容許屬性名或方法名使用[]包圍的表達式,但這時不能省略屬性值。

let obj = {
     * m(){
          yield 'hello world'
     }
}


module.export = {add, remove}

name

對象方法也有name,同函數name狀況,可是這裏,若是是Symbol,返回的是Symbol描述。若是是set、get方法,返回加上前綴set get

Object.assign

Object.assign 第一個參數是目標對象,沒法轉爲對象(undefined\null)時會報錯,源對象是任意,當是數字、布爾值、undefined\null時,忽略。

Object.assign 拷貝自身可枚舉的對象屬性、Symbol屬性,淺拷貝

Object.assign 一個參數時,返回自身

Object.assign(true) // Boolean {[[PrimitiveValue]]: true}
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]

克隆對象

Object.assign({}, origin)

克隆繼承屬性

function clone(origin){
     originObj = Object.getPrototypeOf(origin)
     return Object.assign(Object.create(originObj), origin)
}

枚舉

JSON.stringify() 串行化對象自身可枚舉屬性

getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor([], 'length').enumerable
// false 我覺得要在原型上取到length,由於Object.getOwnPropertyDescriptor({},"toString」)// null

遍歷

  • for…of 包括繼承的全部可枚舉的屬性,不包含Symbol
  • Object.keys 全部自身可枚舉的屬性,不包含Symbol
  • Obejct.getOwnPropertyNames 自身全部屬性,包括不可枚舉,不包含Symbol
  • Object.getOwnPropertySymbols(obj)
  • Reflect.ownKeys 返回全部自身屬性的數組,不論是不是可枚舉、Symbol

遍歷次序

按數字排序;字符串屬性,按生成時間排序;Symbol屬性,按生成時間排序

Object.is

Same-value equality,相對於===,但NaN等於自身,+0不等-0

本身實現以下

Object.defineProperty(Object, ‘is’, {
     value: function(x, y){
          if(x === y){
               return     x ===0 || 1/x === 1/y
          }
          return x !==x && y !==y
     },
     configurable: true,
     enumerable: false,
     writable: true
})

__proto__

es6附錄中,只規定瀏覽器端支持,爲了保險,仍是用
Object.setPrototypeOf`` Object.getPrototypeOf Object.create

Object.getPrototypeOf({__proto__: null})// null

Object.values

Object.values es7 提案,取值規則、順序同keys,

Object.values(2)//[] 
Object.values(true)//[]
Object.keys(2)//[]

Object.entries同 keys

var obj = { foo: 'bar', baz: 42 };
var map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

對象的擴展運算符

es7的提案,試了下,在chrome 52中不能識別

Rest解構賦值

let { x, y, ...z } = null; // 運行時錯誤
let { ...x, y, z } = obj; // 句法錯誤

淺拷貝

let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2

不會拷貝繼承自原型對象的屬性

function baseFunction({ a, b }) {
  // ...
}
function wrapperFunction({ x, y, ...restConfig }) {
  // 使用x和y參數進行操做
  // 其他參數傳給原始函數
  return baseFunction(restConfig);
}

擴展運算符

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
// 等同於
let aClone = Object.assign({}, a);

let ab = { ...a, ...b };
// 等同於
let ab = Object.assign({}, a, b);


let newVersion = {
  ...previousVersion,
  name: 'New Name' // Override the name property
};

let emptyObject = { ...null, ...undefined }; // 不報錯

擴展運算符的參數對象之中,若是有取值函數get,這個函數是會執行的。

// 並不會拋出錯誤,由於x屬性只是被定義,但沒執行
let aWithXGetter = {
  ...a,
  get x() {
    throws new Error('not thrown yet');
  }
};

// 會拋出錯誤,由於x屬性被執行了
let runtimeError = {
  ...a,
  ...{
    get x() {
      throws new Error('thrown now');
    }
  }
};

let const

let 塊做用域內聲明,不存在變量提高,打破了typeof在es5語法中永不報錯,

{
     typeof varmy // nodeJs端報錯,ReferenceError: varmy is not defined
     let varmy
}
typeof unbar  //不報錯,結果undefined

不能重複聲明

{
     var x = 1
     let y = 2
}
function foo(x=y, y=1){
    return {x,y} 
}

const命令聲明時就需賦值,不然報錯,後續不能改值,若是指向對象,只要指向對象地址不變,均可以。其它同let。

若是想凍結對象,Object.freeze。

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, value) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};

es5中兩種聲明變量,var function
es6中 新增let const class import

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

瀏覽器端let const class聲明的變量,再也不是全局對象的屬性

Module

瀏覽器端的模塊化方案AMD,NodeJs端的CommonJs;都是運行時加載。ES6的模塊化是編譯時加載。

ES6的模塊自動採用嚴格模式

  • 變量必須聲明後使用
  • 不能使用with語句
  • 不能使用arguments.callerarguments.callee
  • 增長了保留字,protectedstaticinterface
  • 不能對只讀屬性賦值,不能刪除變量,不然都會報錯
  • 其它

語法

export

export var i = 1
export function fn(){}
export class Person{}

// 或者先定義,後扔出
export {i, fn, Person}

// 重命名
export {
    i as j,
    fn as fn1,
    Person as Man
}

export導出的變量,值是動態綁定的;export必須處於模塊的頂層,不然報錯

import

大括號裏的變量名和要導出文件裏的變量名要一致;或者使用as重命名。import具備命名提高效果。

import能夠只執行後面文件

import 'exec.js'

先導入後導出,能夠簡寫

import {yo} from "yo.js"
export default yo

// 簡寫後
export {yo as default} from "yo.js"

export default

導出的時候,能夠只導出一個默認值,這樣import時,不用關心要導入文件的對應的方法名和變量名,import時,能夠取任一名字。

// yo.js
function fn(){
    //...
}
export default fn

import method1 from 'yo.js'

除了這個特性外,default至關於一個特殊的名稱,擁有通常名稱所擁有的行爲。

// yo.js
function fn(){
    // ...
}
export {fn as default}

import {default} from 'yo.js'

其它

export default var a = 1;// 會報錯
export default function fn(){}
import customName, { otherMethod } from './export-default';
export default 42;

import * as obj from 'yo.js'

模塊的繼承

export * from 'yo.js'

export * 不會導出default

導出的變量,是動態的,變量的值變,導出的結果就變。若是導出是默認,默認值等於一個變量,變量的值變,默認值不變。若是導出的是個引用,不能對引用從新賦值

// lib.js
export let obj = {};

// main.js
import { obj } from './lib';

obj.prop = 123; // OK
obj = {}; // TypeError
相關文章
相關標籤/搜索