ECMAScript 6新特征印象之一:新语法

前记

根据计划,来岁年中,ECMAScript 6(ES6)就要正式宣布了。

近来抽闲看了Dr. Axel Rauschmayer的几篇文章和演讲PPT,对新特征有了些相识。

趁没忘,抓紧记录下,混合自身的感觉。

计分别三部份:

  1. 新语法
  2. 面临对象和模块化
  3. 规范库扩大

参考了以下文章/PPT:

其他文章:

整体印象

的确是「design by champions」。种种为了代码誊写效力举行的优化,自创了最近几年种种「新」言语的优异特征,灵活性大大提拔,浏览难度也提拔了……

不过,熟习Ruby的看了这些会宁神不少吧。

新语法

1.块级作用域 关键字let, const

function order(x, y) {
    if (x > y) {
        let tmp = x;
        x = y;
        y = tmp;
    }
    console.log(tmp === x); // 援用毛病:tmp此时未定义  
    return [x,y];
}

JS终究有了块级作用域变量。虽然在代码构造层面没有太大的作用(之前没偶然也活得很好么,虽然不怎么惬意),但会让代码越发正确,更易于浏览。

今年夏天宣布的Swift中也增加了let关键字,虽然有些许区分,但目标应当是差不多——提拔代码可读性。

2.对象字面量的属性赋值简写 property value shorthand

let first = 'Bob';
let last = 'Dylan';
let singer = { first, last };
console.log(singer.first + " " + singer.last); // Bob Dylan

这关于常常运用对象作为设置属性参数的苦主来讲,算个小小的劝慰了。预计反复增加统一属性会报错吧,没有考证。

3.要领定义 Method definitions

let obj = {
    myMethod(arg0, arg1) {
        ...
    }
};

防止了在对象定义中涌现function关键字,越发清楚明确地星散出函数的三种用处。

4.赋值解构 Destructuring

let singer = { first: "Bob", last: "Dylan" };
let { first: f, last: l } = singer; // 相当于 f = "Bob", l = "Dylan"

依旧是为了轻易。今后代码头部的「变量定义地区」不会有太多行了。

数组也是能够的,下面这个例子迥殊棒:

let [all, year, month, day] =
    /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2014-08-31");

let [x, y] = [1, 2, 3]; // x = 1, y = 2

固然也能够如许,但有些……:

function f([x]) {...} // 参数定义
f(['Blonde on Blonde']);

下面是几种毛病用法(Refutable):

let { a: x, b: y } = {a: 3}; // TypeError
let [x, y] = ['a']; // TypeError

更重要的是,支撑默许值,在情势不婚配或目标值undefined时有用:

let { a: x, b: y=5 } = {a: 3, b: undefined }; // x = 3, y = 5
let [x, y='b'] = ['a']; // x = 'a', y = 'b'

5.函数的多项返回值 Multiple return values

function findSong(songs, songTitle) {
    for (let trackNumber = 0; trackNumber < songs.length; trackNumber++) {
        let song = songs[trackNumber];
        if(songTitle ===song.title) {
            return {song, trackNumber};
        }
    }
    return {song: undefined, trackNumber: -1}
}

let songList = ["Tombstone blues", "Don't think twice", "North country girl"];

let {song, trackNumber} = findSong(songList, "North country girl"); // song = "North country girl", trackNumber = 2;

由于赋值解构,所以也能够如许:

let {song} = findSong(...);
let {trackNumber} = findSong(...);
let {trackNumber, song} = findSong(...); // 变量递次不重要

实在就是返回个对象。

但也有个题目,变量名肯定要与函数返回对象的属性名雷同,这能够会是一个别扭点。

6.函数参数 – 默许值

function findArtist(name='', genre='') {
    ...
}

没什么好说的,今后不必再写var option = option || {}了。

7.函数参数 – 参数打包 Rest parameters

function createArtistProfile(name, ...details) {
    .. // details是个数组
}

所以,今后也不须要arguments了。不过,看例子只是「1,rest」,不知可不能够「1,2,3,rest」。

8.函数参数 – 数组睁开 Spread parameters

Math.max(...[1,11,111]); // 111

算是参数打包的逆操纵,今后不必写[1,2,3].apply(Math.max)这类代码了。

9.函数参数 – 指名参数 Named parameters

function func(arg0, {opt1, opt2}) {
    return [opt1, opt2];
}

func(0, {opt1: 'a', opt2: 'b'}) // ['a', 'b']

一样是经由过程对象带来的变化。有个复杂点的例子:

class Entries {
    // ...
    selectEntries({ from = 0, to = this.length } = {}) {
    // Long: { from: from=0, to: to=this.length }

        // Use `from` and `to`
    }
}

let entries = new Entries();
entries.selectEntries({ from: 5, to: 15 });
entries.selectEntries({ from: 5 });
entries.selectEntries({ to: 15 });

指名参数+赋值解构+默许参数,看着反而有点杂沓了……自由度大自然带来浏览难度的上升,这又是一个衡量点。

10.胖箭头函数 Arrow functions

let bob = {
    name: "Bob Dylan",

    holdConcert: function (songList) {
        songList.forEach(song => {
            console.log(this.name + " sang " + song)
        });
    }
}

这里情势上自创了CoffeeScript里「fat arrow」(ES6对实行和内存上有优化)。Arrow functions重要做了两件事:

  1. 简化了代码情势,默许return表达式效果。
  2. 自动绑定语义this,即定义函数时的this。如上面例子中,forEach的匿名函数参数中用到的this

来看几个例子:

let squares = [ 1, 2, 3 ].map(x => x * x);

x => x + this.y
// 相当于
function(x) { return x + this.y }.bind(this)
// 但胖箭头在实行效力上会更高

胖箭头函数与一般函数的区分:

  1. 胖箭头在建立时即绑定this(lexical this);一般函数的this是在实行时动态传入的(dynamic this)。
  2. 胖箭头没有内部要领[[Construct]]和属性原型,所以new (() => {})是会报错的。
  3. 胖箭头没有arguments变量。

如许,今后在定义要领/函数时,就有了清楚的挑选:

  1. 定义子程序(subroutine),用胖箭头,自动取得语义this。
  2. 定义要领(method),用一般函数,动态this。而且能够用要领定义特征简写代码,防止function关键字涌现。

11.字符串模板 Template strings

templateHandler`Hello ${first} ${last}!`

${first}如许的构造在Ruby的字符串处置惩罚很罕见,first是动态替代的部份。templateHandler是替代后的处置惩罚函数。

固然也能够不要handler,那就仅仅是模板替代了:

if(x > MAX) {
    throw new Error(`At most ${MAX} allowed: $(x)!`);
}

Template strings支撑多行,其间的文本也不会被转码:

var str = String.raw`This is a text
with multiple lines.

Escapes are not interpreted,
\n is not a newline.`;

连系差别的handler,用法多样,比方正则:

let str = "Bob Dylan - 2009 - Together Through Life";
let albumInfo = str.match(XRegExp.rx`
    ^(?<artist>[^/]+ ) - (?<year>\d{4}) - (?<albumTitle>[^/]+)$
`);
console.log(albumInfo.year); // 2009

12.迭代器 Iterators

轻微熟习函数式编程(Python,Ruby也能够)的朋侪对着这个观点应当都不生疏。ES6参考了Python的设想,迭代器有个next要领,挪用会返回:

  1. 返回迭代对象的一个元素:{ done: false, value: elem }
  2. 假如已到迭代对象的末尾:{done: true[, value: retVal] }

上面第二种状况中的前提返回部份是为了递归挪用生成器而设想的(迭代器实际上是生成器的运用之一),详细申明拜见这篇文章的对应部份

下例完成了一个数组的迭代器:

function createArrayIterator(arr) {
    let index = 0;
    return {
        next() {
            if (index < arr.length) {
                return { done: false, value: arr[index++]  };
            else {
                return { done: true }
            }
        }
    }
}

let arr = [1,2,3];
let iter = createArrayIterator(arr);
console.log(iter.next());  // 1
console.log(iter.next());  // 2

在ES6中,可迭代数据构造(比方数组)都必须完成一个名为Symbol.iterator的要领,该要领返回一个该构造元素的迭代器。注重,Symbol.iterator是一个SymbolSymbol是ES6新到场的原始值范例。

针对可迭代的数据构造,ES6还引入了一个新的遍历要领 for-of。再举个例子,革新下上例中的createArrayIterator

function createArrayIterator(arr) {
    let index = 0;
    return {
        [Symbol.iterator]() {
            return this; // 由于自身就是个迭代器
        },
        next() {
            ...
        }
    }
}

let arr = [1, 2, 3];
for(x of createArrayIterator(arr)) { // 注重看
    console.log(x);
}

固然,ES6中的数组自身就是可迭代的,上例仅仅是为了展现罢了。

13.生成器 Generators

ES6的生成器一样自创了Python,经由过程操纵符yield挂起继承

生成器的写法比较奇异,运用了关键字function*

function* generatorFunction() {
    yield 1;
    yield 2;
}

生成器返回一个对象,用来掌握生成器实行,这个对象是可迭代的:

let genObj = generatorFunction();
genObj.next(); // { done: false, value: 1 }
genObj.next(); // { done: false, value: 2 }
genObj.next(); // { done: true }

下面这个例子演示了可递归挪用的生成器,用到了操纵符yield*

function* iterTree(tree) {
    if (Array.isArray(tree)) {
        for (let i = 0; i < tree.length; i++) {
            yield* iterTree(tree[i]);  // (*)
        }
    } else {
        yield tree;
    }
}

yield*会交出(yield)悉数迭代对象,而不仅仅是一个元素值。原话是「yield* in line (*) yields everything that is yielded by the iterable that is its operand. 」

yield*还能够通报返回值。如:

let result1 = yield* step(); // step也是个generator

这个例子不太好,或者说,ES6的这部份完成有点烦琐,须要更多示例才明白这个特征。

    原文作者:皓矾
    原文地址: https://segmentfault.com/a/1190000000656959
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞