es6 笔记

Posted on July 5, 2015

let const

let

  • let 块级作用域

    只在当前大括号内有效

    for循环计数器, 非常适合, 每单次循环, 都有一个全新的计数器变量, 这点和var非常不同

  • let 不存在变量提升, 而var是有的

  • 暂时性死区

    在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)

  • 不能重复声明

  • ES6允许块级作用域的任意嵌套: {{{{{let insane = 'Hello World'}}}}};

  • 块级作用域使得获得广泛应用的立即执行匿名函数不再必要了:

    //匿名函数写法
    (function () {
      var tmp = ...;
      ...
    }());
    
    // 块级作用域写法
    {
      let tmp = ...;
      ...
    }
    

const

  • 严格模式下, 声明的同时需要赋值, 重复赋值报错

    普通模式下, 重复赋值不会报错, 只是无效而已

  • 对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址。const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变

    如果真的想将对象冻结,应该使用Object.freeze方法: const foo = Object.freeze(someObject);

  • 其他性质同let

    • const的作用域与let命令相同:只在声明所在的块级作用域内有效

    • const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用

    • const声明的常量,也与let一样不可重复声明

  • let命令、const命令、class命令声明的全局变量,不属于全局对象的属性


变量的解构赋值

目前的nodejs版本5.6还不支持

关于es6特性, node版本, v8的关系:

ES6 features aren’t in Node core. They’re in v8,

ES6 destructuring(数组的解构赋值 ) is not shipped yet in the V8 version that we have in node v4. However you can make your sample work if you run node with the flag –harmony-destructuring. Be aware that this feature may have bugs or be incomplete (this is why it’s behind a flag).

Most of the information you’d want about ES6 and Node can be found at https://nodejs.org/en/docs/es6/. In a nutshell, ES6 features aren’t in Node core. They’re in v8, the underlying JavaScript engine used by Chrome, Opera, and Node.js. So Node won’t ship with a feature before v8 ships with it. Here’s the v8 issue for the destructuring feature: https://code.google.com/p/v8/issues/detail?id=811


字符串的扩展

  • 字符串添加了遍历器接口, 可以被for...of循环遍历

  • 3个谓词判断函数, 都支持第二个参数,表示开始搜索的位置。

    var s = 'Hello world!';
    
    s.startsWith('Hello') // true
    s.endsWith('!') // true
    s.includes('o') // true
    
    s.startsWith('world', 6) // true
    s.endsWith('Hello', 5) // true
    s.includes('Hello', 6) // false
    
  • 模板字符串

    这是除了生成器外, 我最喜欢的es6特性, 简单实用

    • 反引号可以跨多行, 多行内所有的空格和缩进都会被保留在输出之中
    • ${expression} 可以是任意表达式
  • 标签模板 TODO 感觉没啥用

  • 解决单双引号混用

  • 支持多行文本

    适合写HTML


Symbol

  • 第七种类型, primitive, 表示独一无二的值

  • 创建: Symbol('可选描述字符串'), 不进行登记

    这样创建的任何2个Symbol都不会相等

  • 通过描述, 搜索已有全局环境(就是for环境)Symbol, 否则就创建并登记: Symbol.for('可选描述字符串')

    Symbol.keyFor仅搜索, 不登记

  • 作为属性key, Symbol是不可枚举, 以下方式无法获得Symbol的属性:

    Object.keys()Object.getOwnPropertyNames(), for ...in for...of

    以下方式可以获取:

    • Object.getOwnPropertySymbols
    • Reflect.ownKeys(object)
  • 应用:

    • 消除魔术字符串, 对于那种无所谓内容的字符串
    • 防止属性名的冲突

函数扩展

  • 终于有默认值了, 再也不用变通了: function log(x, y = 'World') {...}

    参数变量是默认声明的,所以不能用let或const再次声明

  • 函数length属性失真: 只返回没有默认值参数的个数

  • 箭头函数: var func = (参数列表) => { 函数语句 }

    对于只有一个参数的参数列表, 或者一句的函数语句, 括号可以省略

    省略{}的单条函数语句, 会自动return, 如果{}不省略, 那么没有自动的return

    用于化简回调: [1,2,3].map(x => x * x);

  • 不可以当作构造函数; 不可以使用arguments对象; 不可以使用yield命令

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象; 和有无{}无关

    this对象的指向是可变的,但是在箭头函数中,它是固定的

    var x = {};
    x.x = function () { return this } //x
    x.x = () => { return this }      //global
    

对象扩展

  • 属性的简洁表示: var foo = 'bar'; var baz = {foo}; 等价于 var baz = {foo: foo};

    function f(x, y) {
        return {x, y};
    }
    
  • 对象方法简写:

    var o = {
      method() { return "Hello!"; }
    };
    
    //Generator 前面加星号
    var obj = {
      * m(){ yield 'hello world'; }
    }
    
  • 对象字面量key的动态计算(和lua一样, 不过有没有卵用还有待检验):

    let propKey = 'foo';
    
    let obj = {
      [propKey]: true,
      ['a' + 'bc']: 123
    };
    

Iterator

  • 规格描述:

    interface Iterable {                     //部署了遍历器接口的数据结构规格
      [Symbol.iterator]() : Iterator,        //需要返回迭代器
    }
    
    interface Iterator {                    //迭代器规格
      next(value?: any) : IterationResult,  //迭代器必须有next方法
    }
    
    interface IterationResult {             //迭代器返回值规格
      value: any,
      done: boolean
    }
    
  • 遍历器接口: 支持遍历器数据结构的Symbol.iterator属性, 返回遍历器接口, 是一个函数, 调用将返回一个遍历器

  • 遍历器: 调用遍历器接口返回遍历器, –for...of

  • 对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法

    NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

  • 遍历器适用的其他api:

    • 解构赋值: let [x,y] = new Set().add('a').add('b').add('c')
    • 扩展运算符(…): ['a', ...arr, 'd']
    • yield*
    • Array.from()
    • Map(), Set(), WeakMap(), WeakSet()
    • Promise.all(), Promise.race()
  • 原生具备iterator接口的数据结构

    • 数组的entries()、keys()和values(), 返回值是迭代器
    • 字符串: someString[Symbol.iterator]()

    可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的

  • for…of

    of 需要一个部署了遍历器接口的数据结构, 如:

    function Iterable(){
      var index = 0;
      var iterable = {};
      iterable[Symbol.iterator] = function () {
          var iterator = {
            next: function(){
              return {value: index++, done: false};
            }
          };
          return iterator;
      };
      return iterable;
    }
    
    var iterable = Iterable();
    
    for (var item of iterable) {
      console.log(item); //无限输出
    }
    
  • 原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构

    • 数组

      数组是Iterable, for(let v of arr)for(let v of arr[Symbol.iterator]())等价

      原生js for (key in arr) 获取键名, for (value of arr) 获取键值


Generator

  • 生成器函数同其他函数一样, 中如果没有显示return, 将有一个隐式return undefined, 因此这种生成器最后一次next将是{ value: undefined, done: true }

  • 对于{ value: XXX, done: true }的next值, 不会出现在for v in gen()的值v里, 也就是done: true 的value不被遍历, 对显示和隐式的return都一样

  • 生成器就是一个迭代器, 生成器也是具备Iterator接口的数据结构

    function* gen(){
      // some code
    }
    var g = gen();
    g[Symbol.iterator]() === g
    // true
    
  • Generator.prototype.throw()

    • Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获

      执行流程会因为生成器在外部的throw, 而回到生成器里, 对执行流程的影响等价于next

    • 如果Generator函数内部没有部署try…catch代码块,那么throw方法抛出的错误,将被外部try…catch代码块捕获

    • Generator函数内抛出的错误,也可以被函数体外的catch捕获

      此时执行流程转交外部

      一旦Generator执行过程中抛出错误,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象,即JavaScript引擎认为这个Generator已经运行结束了

  • Generator.prototype.return(somevalue)

    返回{ value: "somevalue", done: true }, 生成器终止

    如果生成器中有finally return方法会推迟到finally代码块执行完再执行 TODO

  • yield*

    • 对外部生成器对象执行next, 会透明传递给内部yield*调用的其他生成器对象:

      function* inner() {
        yield '1';
        yield '2';
      }
      
      
      function* outer2() {
        yield 'open'
        yield* inner()
        yield 'close'
      }
      
      var gen = outer2()
      console.log(gen.next().value) // "open"
      console.log(gen.next().value) // "1"
      console.log(gen.next().value) // "2"
      console.log(gen.next().value) // "close"
      
    • for of等会递归遍历内外生成器

      yield*不过是for…of的一种简写形式

    • 任何数据结构只要有Iterator接口,就可以被yield*遍历

      let read = (function* () {
        yield 'hello';
        yield* 'hello';
      })();
      
      read.next().value // "hello"
      read.next().value // "h" 字符串也是可遍历对象
      
    • 如果被代理的Generator函数有return语句,那么就可以向代理它的Generator函数返回数据

      这点很重要

  • Generator函数总是返回一个遍历器,ES6规定这个遍历器是Generator函数的实例,也继承了Generator函数的prototype对象上的方法


其他

  • Object.getOwnPropertyNames VS Object.keys

    区别: http://stackoverflow.com/questions/22658488/object-getownpropertynames-vs-object-keys

    如果正常定义的熟悉, 如a.test=21 两者没区别

    使用defineProperties定义的有区别, Object.getOwnPropertyNames得到所有自有熟悉, Object.keys得到可枚举的自有属性

    var a = {};
    Object.defineProperties(a, {
        one: {enumerable: true, value: 'one'},
        two: {enumerable: false, value: 'two'},
    });
    Object.keys(a); // ["one"]
    Object.getOwnPropertyNames(a); // ["one", "two"]
    
  • Javascript 严格模式详解

  • ES6 特性分为:

    • shipping: 当前V8支持, 默认开启, 无需参数

    • staged: 基本完成, 但是可能不稳定, 需要使用参数--harmony开启

      别名 --es_staging

    • in progress: 可以通过特定harmony参数激活, 但是不建议使用, 不稳定

      查看in progress的特性: node --v8-options | grep "in progress"

  • nodejs ES6 进展情况: http://node.green/

  • 查看当前V8 版本: node -p process.versions.v8


参考资料

2016-11-7 补充: