ES6内容

iterator

遍历器iterator

makeIterator是个遍历器,生成遍历器对象it

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接口

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

iterable接口为数据结构提供统一的数据访问机制,具有线性访问特点。

原生部署的有:数组、类似数组对象、Set和Map,数组、Set、Map的entries、keys、values方法的返回。
类似数组对象如 arguments对象 DOM NodeList对象;

对象部署Iterator接口:

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.trace

for…of 对比for…in

  • for…in

    获取键名,无序的,循环对象所有可枚举的属性,作用数组时,返回的key是字符串

  • for…of

    of后面跟遍历器对象,或者部署了遍历器接口的数据类型。返回有序元素,相对与forEach可以break、continue

    如果跟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
posted @ 2016-11-24 10:43  小方。  阅读(1166)  评论(0编辑  收藏  举报