澳门新葡萄京官网注册 1

澳门新葡萄京官网注册ES6

本文由码农网 –
小骆原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

你可能已经听说过ECMAScript 6(简称 ES6)了。ES6 是 Javascript
的下一个版本,它有很多很棒的新特性。这些特性复杂程度各不相同,但对于简单的脚本和复杂的应用都很有用。在本文中,我们将讨论一些精心挑选的
ES6 特性,这些特性可以用于你日常的 Javascript 编码中。

本文是ES6系列的第二篇,主要介绍ES6中对现有对象方法属性的拓展,先上传送门:

你可能已经听说过EMCAScript6(ES6)了,这是下一个版本的Javascript,它包含了一些很棒的新特性。这些特性拥有不同程度的复杂性,对于简单的脚本和复杂的应用程序都非常的有用。本文将盘点一些ES6的新特性,这些特性都可以用在你日常的编码中。

请注意,当前浏览器已经全面展开对这些 ES6
新特性的支持,尽管目前的支持程度还有所差异。如果你需要支持一些缺少很多
ES6 特性的旧版浏览器,我将介绍一些当前可以帮助你开始使用 ES6
的解决方案。

  • 1 变量部分
  • 2 现有对象拓展
  • 3
    新增数据类型/数据结构
  • 4 新的异步编程模式
  • 5 类和模块

澳门新葡萄京官网注册,请注意,只有现代浏览器才能支持这些新的ES6特性,虽然浏览器的支持各不相同。如果你需要兼容那些不支持ES6新特性的旧浏览器,我也会谈谈关于这方面的解决方案。

文中大多数代码示例都带有“运行代码”链接,你可以查看代码并运行它。

1 增加了模板字符串

先看一下,ES6之前我们是如何实现输出模板的:

    document.getElementById('root').innerHTML = "the user name is " + username + "nthe user age is  " + age;

这样通过字符串相加拼接确实挺繁琐的,很容易出错,ES6引入了模板字符串来简化代码,两者输出效果是一样:

    //ES6环境下
    document.getElementById('root').innerHTML = `the user name is ${username}
    the user age is  ${age}`;

通过反引号包裹的字符串来声明模板字符串,插入变量直接通过${变量名}实现,另外要注意模板字符串中的所有空格和换行都是被保留的。

${变量名}中大括号内不仅支持变量名,对于任意的JavaScript表达式也是支持的,例如可以这样用:

    var result =  `my name is ${(function(){return 'vicfeel';})()}`;

在本文中,大部分示例代码都会带有“运行代码”的链接,读者可以点击链接运行示例。

变量

2 数组的拓展

  • Array.from( ),将伪数组对象转换为真正的数组

什么是伪数组对象?具有数组的结构,但不是数组对象,不能使用数组方法如forEach等,举几个栗子:

    let fakeArr1 = {
        '0':1,
        '1':2,
        '2':3,
        'length':3
    };

    function f(){
        let fakeArr2 = arguments;
    }

    let fakeArr3 = document.querySelectorAll('div');

    //上面三类都是伪数组对象
    'forEach' in fakeArr1; //false
    let arr = Array.from(fakeArr1); //ES5的写法 var arr = Array.slice.call(fakeArr1);
    'forEach' in arr;   //true
  • Array.find( ),在数组中检索第一个匹配要素

find()参数为一个函数,设置查找条件,看栗子:

    let arr = [1,3,5,7];
    var result = arr.find(function(value, index, arr){
        return value > 4;
    });
    var result2 = farr.find(function(value, index, arr){
        return value > 10;
    });
    console.log(result); //5
    console.log(result2); //找不到返回undefined;

findIndex()方法与find()类似,只不过查找的是序号:

    let arr = [1,3,5,7];
    var result = arr.findIndex(function(value, index, arr){
        return value > 4;
    });
    var result2 = farr.findIndex(function(value, index, arr){
        return value > 10;
    });
    console.log(result); //2
    console.log(result2); //找不到返回-1;
  • Array.fill( ),给定一个值来填充数组

    let arr = [1,2,3];
    arr.fill(5); //[5,5,5]
    
    //fill也可以接收3个参数,第二个和第三个参数分别为填充开始的位置和结束的位置
    let arr2 = [1,2,3,4,5,6];
    arr2.fill(5,1,3); //[1,5,5,4,5,6]
    

澳门新葡萄京官网注册 1

LET

3 函数增加默认参数

ES6之前的函数是无法带有默认参数的,我们通常采用以下方式实现默认参数设置:

    function f(name,age){
        //设置默认值
        name = name || 'defaultName';  
        age = age || 'defaultAge';
    }

ES6中提供了新的方法:

    //ES6环境下
    function f(name,age = 23){
        console.log(name + ',' + age);
    }
    f('vicfeel');  //vicfeel,23

通过Babel可以将ES6代码转换为浏览器支持ES5代码,这实际上是用ES5来模拟的一个过程,可以帮助我们了解ES6该方法的实现原理:

    //Babel转换后
    function f(name) {
        var age = arguments.length <= 1 || arguments[1] === undefined ? 23 : arguments[1];

        console.log(name + ',' + age);
    }
    f('vicfeel'); //vicfeel,23

从上面可以看出,如果第二个参数严格等于“===”undefined就使用默认参数,这实际上在原有函数的基础上对形参加了一层解析赋值(见上一篇中的变量解析赋值)。

变量

你习惯于用var声明变量。现在你也可以用let了。两者微妙的差别在于作用域。var声明的变量作用域为包围它的函数,而let声明的变量作用域仅在它所在的块中。

4 函数新增rest参数

ES6引入了rest参数(“…变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了,看个栗子:

    function f(...vals){
        console.log(vals);  //[1,2,3]
    }

    f(1,2,3);

上例中的vals类型为Array,值为[1,2,3],可以看成将arguments转换为数组后的结果,而且要比arguments有更高的灵活性,rest参数还可以这样用:

    //ES6环境下
    function f(v,...vals){
        console.log(v);   //'temp'
        console.log(vals); //[1,2,3]
    }

    f('temp',1,2,3);

    //ES5通过arguments的模拟
    function f(v) {
        console.log(v);

        for (var _len = arguments.length, vals = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            vals[_key - 1] = arguments[_key];
        }

        console.log(vals);
    }

    f('temp', 1, 2, 3);

另外在使用rest时要注意的一点,rest参数后不可以再加别的参数:

    function f(...vals,v){ }  //这种方式会报错

除了在函数中作为rest参数,“ ... ”本身可以作为一个运算符使用,用处与rest参数刚好相反,是将一个数组转为用逗号分隔的参数序列,看看栗子:

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

    var arr = [23,12];
    add(...arr); //35

    Math.max(...[4, 13, 15]) // 等同于Math.max(4, 13, 15);

    //结合rest使用
    function f(...vals){
        vals //[1,2,3]
    }

    var arr = [1,2,3];
    f(...arr);
    //当然上面这样用是多次一举,转换为参数再转回来,目的是为了理解两者是互为逆操作的

    //其它用法
    var nodeList = document.querySelectorAll('div');
    var array = [...nodeList];

    var arr1 = [1,2,3],arr2 = [4,5,6];
    var arr3 = [...arr1,...arr2]; //合并数组,在ES5中我们一般是这样用的arr1.concat(arr2);

LET

通常我们使用var关键字来声明变量,现在我们同样可以使用let,它们之间的细微差别在于作用域。使用var声明变量时,该变量的作用域是其最近的函数,而使用let声明变量,它的作用域只在包含它的块。

if(true) {
   let x = 1;
}
console.log(x); // undefined

这样可以让代码更加干净整洁,可以减少无用的变量。

看看下面这个经典的数组循环:

for(let i = 0, l = list.length; i < l; i++) {
   // do something with list[i]
}

console.log(i); // undefined

举个例子,经常会有人使用变量j在同一作用域中的另外一个循环中。但是使用let声明变量,你可以很安全地再声明一次,因为它只在自己块级作用域内定义和有效。

if(true){

letx=1;

}

console.log(x);// undefined

5 增加箭头=>函数

直接用Babel转换“var f = a => b”为ES5看了一下

    //var f = a => b;

    var f =function (a) {
      return b;
    };

这样一下就明了了,我们可以将箭头函数理解成一种语法糖,是对函数的一种简化,a为参数,b为返回值

看一下复杂的用法:

    //当传入多个参数或对象时,要用()包裹
    var add = (a,b) => a + b
    //等同于
    var add = function(a,b){ return a + b; }

    //传入对象
    var plus = ({name,age}) => name + age;
    var person = {
        name:'Vicfeel',
        age:23
    };
    plus(person); //Vicfeel23

灵活运用箭头函数,可以简化很多操作:

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

    const IsEven = x => x % 2 == 0;

    let arr2 = [12,2,43,3,18];
    arr2.sort((x,y) => x - y);

另外,关于使用箭头函数有一点需要注意的地方:this对象的指向是可变的,但是在箭头函数中,它是固定的,我们结合例子看一下:

    var handler = {
      init: function() {
        document.addEventListener('click',
          e => this.doSomething(e), false);
      },

      doSomething: function(e) {
        console.log('do something);
      }
    };

我们为document绑定了点击事件,回调函数中使用箭头函数,调用handler的doSomething方法,一般的函数在点击执行中this会发生改变,指向document,并报错doSomething未定义,但在箭头函数中this在定义时便是固定的不再改变,将上面的例子转换为ES5看一下:

    //ES5
    var handler = {
        init: function init() {
            var _this = this;

            document.addEventListener('click', function (e) {
                return _this.doSomething(e);
            }, false);
        },

        doSomething: function doSomething(e) {
            console.log('do something');
        }
    };

转换后的ES5代码就清楚地说明了,箭头函数里面根本没有自己的this,而是引用外层的this。

参考Reference

博文作者:vicfeel
博文出处:
本文版权归作者和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作!
如果阅读了本文章,觉得有帮助,您可以为我的博文点击“推荐一下”!

CONST

声明块级作用域内的变量的另一种方法是使用const。使用const,你可以声明一个只读的值,必须直接指定一个值,如果尝试改变它的值或者没有立即指定一个值,就会得到下面的错误:

const MY_CONSTANT = 1;
MY_CONSTANT = 2 // Error
const SOME_CONST; // Error

注意,你还是可以修改对象的属性或者数组的成员

const MY_OBJECT = {some: 1};
MY_OBJECT.some = 'body'; // Cool

这样使得代码更加干净,减少滞留的变量。看看以下经典的数组遍历:

箭头函数

箭头函数对于Javascript来说是一个非常棒的补充,它可以让代码更加精简。我们首先来介绍箭头函数,在稍后的其他例子中就会使用到它的优点。下面的代码展示了一个箭头函数和我们熟悉的ES5风格的两种写法的函数:

let books = [{title: 'X', price: 10}, {title: 'Y', price: 15}];

let titles = books.map( item => item.title );

// ES5 equivalent:
var titles = books.map(function(item) {
   return item.title;
});

运行代码%3B)

我们来看看箭头函数的语法,其中没有function关键字,剩下的就是0个或多个参数、(=>)箭头和函数表达式。注意:return语句将隐式地被添加进来。

如果是0个或多个参数,必须添加括号:

// No arguments
books.map( () => 1 ); // [1, 1]

// Multiple arguments
[1,2].map( (n, index) => n * index ); // [0, 2]

如果需要更多的逻辑或者空白区域,可以将函数表达式放在({…})块中。

let result = [1, 2, 3, 4, 5].map( n => {
   n = n % 3;
   return n;
});

运行代码%3B)

箭头函数不仅仅意味着更少的字符,它的行为也不同于常规的函数。一个箭头函数从它的外界上下文中继承this和arguments关键字。这表示你可以摆脱以前那些难看的语句,比如var
that =
this,而且不需要绑定函数到正确的上下文中。下面有一个例子(注意:this.title等同于ES5版本的that.title):

let book = {
   title: 'X',
   sellers: ['A', 'B'],
   printSellers() {
      this.sellers.forEach(seller => console.log(seller + ' sells ' + this.title));
   }
}

// ES5 equivalent:
var book = {
   title: 'X',
   sellers: ['A', 'B'],
   printSellers: function() {
      var that = this;
      this.sellers.forEach(function(seller) {
         console.log(seller + ' sells ' + that.title)
      })
   }
}

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.sellers.forEach(seller%20%3D%3E%20console.log(seller%20%2B%20%27%20sells%20%27%20%2B%20this.title))%3B%0A%20%20%20%20%7D%0A%7D)

for(leti=0,l=list.length;i

// do something with list[i]

}

console.log(i);// undefined

字符串

举个例子,通常情况下,我们在同一作用域里使用变量j来完成另一个遍历。但是,现在有了let,可以安全地再一次声明i变量。因为它只在被声明的块中有效。

方法

String的prototype中添加了几个方便的方法,大部分是indexOf方法的变通:

'my string'.startsWith('my'); //true
'my string'.endsWith('my'); // false
'my string'.includes('str'); // true

简单有效!另外,还添加了一个方便创建重复字符串的方法:

'my '.repeat(3); // 'my my my '

CONST

模板字符串

模板字符串提供了一个简洁的方式去创建字符串和实现字符串插值。你可能已经熟悉了它的语法,模板字符串基于美元符号和花括号
${…},并且要使用反引号(`)将其包围。

下面是一个简单的演示:

let name = 'John',
   apples = 5,
   pears = 7,
   bananas = function() { return 3; }

console.log(`This is ${name}.`);

console.log(`He carries ${apples} apples, ${pears} pears, and ${bananas()} bananas.`);

// ES5 equivalent:
console.log('He carries ' + apples + ' apples, ' + pears + ' pears, and ' + bananas() +' bananas.');

运行代码%20%7B%20return%203%3B%20%7D%0A%0Aconsole.log(%60This%20is%20%24%7Bname%7D.%60)%3B%0A%0Aconsole.log(%60He%20carries%20%24%7Bapples%7D%20apples%2C%20%24%7Bpears%7D%20pears%2C%20and%20%24%7Bbananas()%7D%20bananas.%60)%3B%0A)

上面的示例中,和ES5相比较,模板字符串仅仅只是方便字符串的串联。模板字符串通常应用于多行字符串,请记住,空白是字符串的一部分。

let x = `1...
2...
3 lines long!`; // Yay

// ES5 equivalents:
var x = "1...n" + 
"2...n" +
"3 lines long!";

var x = "1...n2...n3 lines long!";

运行代码

还有另一个用于声明块作用域变量的方法。使用const,你可以声明一个值的只读引用。必须直接给一个变量赋值。如果尝试修改变量或者没有立即给变量赋值,都将报错:

数组

Array对象现在新增了一些静态方法以及prototype上的一些方法。

第一、Array.from方法从类数组或可迭代对象上创建Array的实例。类数组对象的例子包括:

1、函数中的arguments对象
2、document.getElementsByTagName放回的一个nodeList对象
3、新的Map和Set数据结构

let itemElements = document.querySelectorAll('.items');
let items = Array.from(itemElements);
items.forEach(function(element) {
    console.log(element.nodeType)
});

// A workaround often used in ES5:
let items = Array.prototype.slice.call(itemElements);

运行代码%3B%0Alet%20items%20%3D%20Array.from(itemElements)%3B%0Aitems.forEach(function(element)%20%7B%0A%20%20%20%20console.log(element.nodeType)%0A%7D)%3B%0A)

上面的示例中,可以看出items数组拥有forEach方法,但是在itemElements集合中,这个方法是不可用的。

Array.from有一个有趣的特性是它的第二个可选参数mapFunction,这个参数允许在单次调用中创建一个新的映射数组。

let navElements = document.querySelectorAll('nav li');
let navTitles = Array.from(navElements, el => el.textContent);

运行代码%3B%0Alet%20navTitles%20%3D%20Array.from(navElements%2C%20el%20%3D%3E%20el.textContent)%3B%0Aconsole.log(navTitles.join(%27%27))%3B)

第二、Array.of方法,这个方法的行为有点像Array的构造函数,它修复了传递单个数字参数时的特殊情况,所以Array.of相比于new
Array()更好。不过大多数情况下,我们推荐使用数组字面量。

let x = new Array(3); // [undefined, undefined, undefined]
let y = Array.of(8); // [8]
let z = [1, 2, 3]; // Array literal

最后,Array的prototype中添加了几个方法,其中的find方法我觉得Javascript开发者将会非常喜欢。

1、find方法:获取回调函数return true的第一个元素。
2、findIndex方法:获取回调函数return true的第一个元素的索引
3、fill方法:根据给定的参数重写数组的元素

[5, 1, 10, 8].find(n => n === 10) // 10

[5, 1, 10, 8].findIndex(n => n === 10) // 2

[0, 0, 0].fill(7) // [7, 7, 7]
[0, 0, 0, 0, 0].fill(7, 1, 3) // [0, 7, 7, 7, 0]

constMY_CONSTANT=1;

MY_CONSTANT=2// Error

constSOME_CONST;// Error

Math

Math对象也添加了几个方法。

1、Math.sign 返回一个数字的符号,有1,-1或0三个值分别表示正值,负值或0
2、Math.trunc 返回一个数字去掉小数位数后的数
3、Math.cbrt 返回一个数字的立方根

Math.sign(5); // 1
Math.sign(-9); // -1

Math.trunc(5.9); // 5
Math.trunc(5.123); // 5

Math.cbrt(64); // 4

如果你想要学习更多的新的Math内容,点击new number and math features in
ES6。

注意,对象的属性或数组成员还是可以改变的。

扩展操作符

扩展操作符(…)是一个非常方便的语法,它用于在数组的特殊的地方扩展元素,比如函数调用中的参数。下面展示一些例子来说明它的用处。

首先,我们来看看如何通过另一个数组来扩展数组的元素:

let values = [1, 2, 4];
let some = [...values, 8]; // [1, 2, 4, 8]
let more = [...values, 8, ...values]; // [1, 2, 4, 8, 1, 2, 4]

// ES5 equivalent:
let values = [1, 2, 4];
// Iterate, push, sweat, repeat...
// Iterate, push, sweat, repeat...

运行代码%3B%0Aconsole.log(more)%3B)

当使用参数调用函数时,扩展操作符同样非常强大。

let values = [1, 2, 4];

doSomething(...values);

function doSomething(x, y, z) {
   // x = 1, y = 2, z = 4
}

// ES5 equivalent:
doSomething.apply(null, values);

运行代码%3B%0Afunction%20doSomething(x%2C%20y%2C%20z)%20%7B%0A%20%20%20%20console.log(arguments)%3B%0A%7D%0A)

正如你所看到的,这避免了我们经常使用的fn.apply()这种委婉曲折的方式。扩展操作符语法非常灵活,因为它可以在参数列表的任何地方使用,即下面的调用方式也会产生一样的结果:

let values = [2, 4];
doSomething(1, ...values);

运行代码%3B%0Afunction%20doSomething(x%2C%20y%2C%20z)%20%7B%0A%20%20%20%20console.log(arguments)%3B%0A%7D)

我们已经将扩展操作符应用到Array和arguents中了。实际上,所有的可迭代的对象都可以应用扩展操作符,比如NodeList:

let form = document.querySelector('#my-form'),
   inputs = form.querySelectorAll('input'),
   selects = form.querySelectorAll('select');

let allTheThings = [form, ...inputs, ...selects];

运行代码

现在allTheThings变成一个扁平的数组,其中包含form节点,input和select的子节点。

constMY_OBJECT={some:1};

MY_OBJECT.some=’body’;// Cool

解构

解构提供了一个便捷的方式来从对象或数组中提取数据。下面给了一个使用数组的典型例子。

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

// ES5 equivalent:
var arr = [1, 2];
var x = arr[0];
var y = arr[1];

使用这种语法,可以一次性指定多个变量。还有另外一个作用是可以很简单的交换两个变量值。

let x = 1,
   y = 2;

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

运行代码

解构也能用于对象上,要保证key值匹配。

let obj = {x: 1, y: 2};
let {x, y} = obj; // x = 1, y = 2

运行代码

也可以通过这个机制来修改变量的名称

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

还有另外一个有趣的用法是模拟多个返回值

function doSomething() {
   return [1, 2]
}

let [x, y] = doSomething(); // x = 1, y = 2

运行代码%20%7B%0A%20%20%20%20return%20%5B1%2C%202%5D%0A%7D%0A%0Alet%20%5Bx%2C%20y%5D%20%3D%20doSomething()%3B)

解构同样也可以指定argument对象的默认值,通过字面量对象,可以模拟命名参数。

function doSomething({y = 1, z = 0}) {
   console.log(y, z);
}
doSomething({y: 2});

箭头函数

参数

箭头函数为 Javascript
语言增色不少。它使得代码更简洁。我们早早地在本文中介绍箭头函数,这样就可以在后面的示例中加以利用了。以下代码片段是箭头函数和我们熟悉的
ES5 版本的写法:

默认值

在ES6中,是可以给函数参数定义一个默认值的,语法如下:

function doSomething(x, y = 2) {
   return x * y;
}

doSomething(5); // 10
doSomething(5, undefined); // 10
doSomething(5, 3); // 15

运行代码%20%7B%0A%20%20%20%20return%20x%20*%20y%3B%0A%7D%0A%0Aconsole.log(doSomething(5))%3B%0Aconsole.log(doSomething(5%2C%20undefined))%3B%0Aconsole.log(doSomething(5%2C%203))%3B)

这样看起来就简洁多了,如果是ES5之前的写法,我们肯定要补充一些参数:

function doSomething(x, y) {
   y = y === undefined ? 2 : y;
   return x * y;
}

undefined或者无参时将会触发参数的默认值。

letbooks=[{title:’X’,price:10},{title:’Y’,price:15}];

lettitles=books.map(item=>item.title);

// ES5 equivalent:

vartitles=books.map(function(item){

returnitem.title;

});

剩余不定参数

我们已经看过了扩展操作符,不定参数与其非常相似。不定参数也使用…语法,它允许将函数末端的参数存储在一个数组里面。

function doSomething(x, ...remaining) {
   return x * remaining.length;
}

doSomething(5, 0, 0, 0); // 15

运行代码%20%7B%0A%20%20%20%20return%20x%20*%20remaining.length%3B%0A%7D%0A%0AdoSomething(5%2C%200%2C%200%2C%200)%3B)

运行代码%3B%0A)

模块

模块是Javascript中非常受欢迎的一个补充,我认为它是ES6中非常值得挖掘的一个特性。

现如今,任何重要的JS项目都会使用某种模块系统-可能是“暴露型模块模式”或者更广泛的AMD和Common.js。但是,浏览器是没有任何模块系统的特性的,总是需要为AMD或CommonJS模块构建加载模块,处理这些的工具包括RequireJS,Browserify和Webpack。

ES6规范中同时包含了模块中的语法和加载机制。如果你想要在以后使用模块,应该使用下面的语法。现代的构建工具可以通过插件支持这种格式,所以我们可以尽管去使用它。(不用担心,我们在后面的“Transpilation”章节中会讨论这个问题)。

现在,在ES6的模块语法中,模块被设计成使用export和import两个关键字,我们来看看示例中的两个模块。

// lib/math.js

export function sum(x, y) {
   return x + y;
}
export var pi = 3.141593;

// app.js

import { sum, pi } from "lib/math";
console.log('2π = ' + sum(pi, pi));

如你所见,代码中有多个export语句。每一个都必须显式地声明输出的值,在这个例子中,就是function和var。

示例中的import语句使用了一个语法(类似于解构)来显式地定义了输出的内容。要将整个模块一起输出,可以使用通配符“*”,结合as关键字给模块一个本地名称。

// app.js

import * as math from "lib/math";
console.log('2π = ' + math.sum(math.pi, math.pi));

模块系统有一个默认模块,它也可以是函数。要导出模块内的默认值,需要提供一个本地名称:

// lib/my-fn.js

export default function() {
   console.log('echo echo');
}

// app.js

import doSomething from 'lib/my-fn';
doSomething();

注意:import语句是同步的,但是它会等到所有依赖的加载完毕才会执行。

如果我们观察箭头函数的语法,会发现其中并没有出现function关键词。只保留零或多个参数,“胖箭头”(=>)和函数表达式。return声明被隐式加入。

Classes

类是ES6中讨论得很多的一个特性。一些人类违反了JS的原型性质,而其他人觉得这降低了初学者和来自其他开发语言的开发者的入门门槛,并且能够帮助他们编写大规模应用。无论如何,它都是ES6中的一部分,我们简单地介绍一下。

我们通过class和constructor关键字构建类,下面是一个简短的示例:

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
let myVehicle = new Vehicle('rocky');

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.name%20%3D%20name%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20%27vehicle%27%3B%0A%20%20%20%20%7D%0A%20%20%20%20getName()%20%7B%0A%20%20%20%20%20%20%20%20return%20this.name%3B%0A%20%20%20%20%7D%20%20%20%0A%7D%0A%0Alet%20myVehicle%20%3D%20new%20Vehicle(%27rocky%27)%3B%0A%0Aconsole.log(myVehicle.getName())%3B%0A)

注意:类的定义并不是一个普通的对象,因此类成员之间没有使用逗号来分隔。

从一个类创建实例必须使用new关键字,而从一个基类继承则使用extends:

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

let myCar = new Car('bumpy');

myCar.getName(); // 'bumpy'
myCar instanceof Car; // true
myCar instanceof Vehicle; //true

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.name%20%3D%20name%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20%27vehicle%27%3B%0A%20%20%20%20%7D%0A%20%20%20%20getName()%20%7B%0A%20%20%20%20%20%20%20%20return%20this.name%3B%0A%20%20%20%20%7D%20%20%20%0A%7D%0A%0Aclass%20Car%20extends%20Vehicle%20%7B%0A%20%20%20%20constructor(name)%20%7B%0A%20%20%20%20%20%20%20%20super(name)%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20%27car%27%0A%20%20%20%20%7D%0A%7D%0A%0Alet%20myCar%20%3D%20new%20Car(%27bumpy%27)%3B%0A%0Aconsole.log(myCar.getName())%3B%0Aconsole.log(myCar%20instanceof%20Car)%3B%0Aconsole.log(myCar%20instanceof%20Vehicle)%3B)

在派生类中,可以使用super关键字来访问基类的构造函数或方法:

1、要访问基类构造函数,使用super()
2、要访问基类中的方法,是用super.getName()

类还有更多的用法,如果想要深入地学习这方面,可以看看Classes in
ECMAScript6

带有零或多个参数时,必须使用括号:

Symbol

Symbol是一种新的原始数据类型,和Number、String一样。我们可以使用symbol来给对象创建唯一的ID或唯一的常量。

const MY_CONSTANT = Symbol();

let obj = {};
obj[MY_CONSTANT] = 1;

注意:Object.getOwnPropertyNames方法不会返回Symbol生成键值,在for..in循环中,Object.keys()和JSON.stringify()也是不可见的,这是与普通的字符串key值的区别所在。我们可以通过Object.getOwnPropertySymbols()获取对象中的symbol数组。

因为不可变的特点,Symbols常常与const一起配合使用:

const CHINESE = Symbol();
const ENGLISH = Symbol();
const SPANISH = Symbol();

switch(language) {
   case CHINESE:
      // 
      break;
   case ENGLISH:
      // 
      break;
   case SPANISH:
      // 
      break;
   default:
      // 
      break;
}

也可以给Symbol一段字符串来描述,虽然无法通过字符串来访问symbol本身,但是调试的时候很有用。

const CONST_1 = Symbol('my symbol');
const CONST_2 = Symbol('my symbol');

typeof CONST_1 === 'symbol'; // true

CONST_1 === CONST_2; // false

想要学习更多的symbol内容可以查看symbol
primitive

// No arguments

books.map(()=>1);// [1, 1]

// Multiple arguments

[1,2].map((n,index)=>n*index);// [0, 2]

Transpilation

现在我们可以使用ES6来写代码了。前面介绍中提到的浏览器还没有广泛地支持ES6的特性,而且支持性各不相同。你的用户使用的浏览器很有可能不完全懂得解析ES6代码。所以我们要将这些代码转换成上一个版本的Javascript(ES5),它们可以很好地运行在现代浏览器上,这种转换通常被称为Transpilation。在浏览器支持ES6之前,都需要在我们的应用程序中做这一项转换。

如果需要写更多的逻辑或更多的空格,可以把函数表达式放在({…})块中。

开始

转换代码并不困难,可以直接通过命令行转换代码,或者在Grunt和Gulp中作为一个Task包含在插件里面。有很多转换代码的方案,比如Babel,Traceur和TypeScript。可以看看这个使用Babel的例子many
ways to start using
ES6,很多ES6的特性都会进行处理。

那我们如何使用ES6呢?首先,根据你想要使用的ES6特性和你需要支持的浏览器或运行环境(比如Node.js),在你的工作流中结合一个编译转换器。如果你希望的话,还有一些监视文件变化和浏览器实时刷新的插件来让你体验无缝的编码。

如果是从头开始,你可能只是想要使用命令行来转换代码(查看例子Babel CLI
documentation)。如果你已经使用过grunt或gulp之类的工具,你可以添加一个比如gulp-babel的插件,或者Webpack中的babel-loader插件。对于Grunt,有一个grunt-babel,还有很多其他的ES6-related
plugins。对于使用Browserify的开发者,可以看看babelify。

很多特性被转换成ES5的兼容性代码后,并不会有很大的开销,通过编译器提供的临时性方案会有一点点的性能损耗。你可以通过各种交互环境(也称作RELPs)来看看使用ES6代码和编译后的代码是什么样的:

1、Traceur:website,REPL
2、Babel:website,REPL
3、TypeScript:website,REPL
4、ScratchJS(chrome插件)

注意,TypeScript并不完全是一个转换器,它是强类型的Javascript的超集,可以编译成Javascript,它和其他转换器一样,支持很多ES6特性。

let result=[1,2,3,4,5].map(n=>{

n=n%3;

return n;

});

究竟如何使用?

通常来说,一些ES6的特性可以自由地使用,比如模块、箭头函数,不定参数和类。这些特性不会用太多开销,就可以转换成ES5代码。而Array、String和Math对象上和原型上的方法(比如Array.from等等)需要所谓的“polyfills”。Polyfills是对那些浏览器还没有原生支持的特性的一个临时方案。你可以首先加载polyfills,如果浏览器有此函数,代码就会正常运行,Babel和Traceur都会提供类似的polyfills。

可以查看ES6兼容性表来看看转换器和浏览器对ES6新特性的支持情况。令人激动的是,在写这篇文章的时候,最新的浏览器已经支持了所有ES6特性的55%到70%。Microsoft
Edge,Google Chrome和Mozilla
Firefox相互竞争,这对整个Web的发展有很大的意义。

就我个人而言,我发现能够很简单地使用ES6中的新特性,比如模块,箭头函数和不定参数等等是一种解脱,也是对自己编码的一个显著的提升。现在我很享受使用ES6写代码,然后将其转换成ES5代码。ES6的优点随着时间的增长会越来越明显。

运行代码%3B)

下一步呢?

只要安装了一个转换器,就可以开始使用一些小的特性,比如let和箭头函数。记住,已经编写好的ES5代码,转换器会原封不动地保留下来。当你使用ES6去优化你的代码,慢慢地喜欢用它,你就可以逐步将越来越多的ES6特性应用到代码中。也许有一些代码会有新的模块或类语法,但是我保证一切都会越来越好的!

除了文章中提到的特性,还有更多的ES6的东西没有被提到,比如Map,Set,标签模板字符串,生成器,Proxy和Promise,如果你想知道请关注后续的文章。另外,如果想要深入学习,我推荐Exploring
ES6这本书,书里面提到了所有的ES6特性。

箭头函数不单只是为了输入更少的字符,它们的表现也和一般函数不同。它继承了当前上下文的this和arguments。这就意味着,你可以避免写var
that =
this这样丑陋的代码,也不需要把函数绑定到正确的上下文了。举例如下(注意对比this.title和
ES5 版本的that.title的不同):

最后的思考

通过使用转换器,所有的代码实际上是转换成了ES5。而浏览器一直在添加新特性,所以,即便浏览器完全支持某个特定的ES6特性,最后还是运行ES5兼容版本的代码,这样可能表现会更糟糕。你可以期待,在你需要兼容的浏览器和运行环境里,所有的ES6特性最终都会被支持。但是在那之前,我们需要管理好这些ES6特性的支持情况,选择性地禁用某些ES6特性来减少转换成ES5代码后带来的不必要的开销。知道了这些,你就可以决定是否要使用ES6中的特性。

let book={

title:’X’,

sellers:[‘A’,’B’],

printSellers(){

this.sellers.forEach(seller=>console.log(seller+’ sells
‘+this.title));

}

译者信息

小骆,90后码农一个,潜水于互联网中,专注web开发,喜爱写代码,个人博客狼狼的蓝胖子

}

// ES5 equivalent:

var book={

title:’X’,

sellers:[‘A’,’B’],

printSellers:function(){

var that=this;

this.sellers.forEach(function(seller){

console.log(seller+’ sells ‘+that.title)

})

}

}

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.sellers.forEach(seller%20%3D%3E%20console.log(seller%20%2B%20’%20sells%20’%20%2B%20this.title))%3B%0A%20%20%20%20%7D%0A%7D)

字符串

方法

几个方便的方法被添加到String的原型中。其中大多数用于简化需要用indexOf()方法来解决的问题的复杂度,并达到同样的效果:

‘my string’.startsWith(‘my’);//true

‘my string’.endsWith(‘my’);// false

‘my string’.includes(‘str’);// true

很简单但是很有效。添加了另外一个方便的用于创建重复字符串的方法:

‘my ‘.repeat(3);// ‘my my my ‘

模板字符串

模板字符串提供一个简洁的方式来实现字符串插值。你可能已经对这种语法很熟悉了;它基于美元符号和花括号${..}。模板字符串置于引号之中。以下是快速示例:

let name = ‘John’,

apples = 5,

pears = 7,

bananas = function(){ return 3 ;}

console.log(` This is ${ name }.`);

console.log(` He carries ${ apples } apples , ${ pears }pears , and
${ bananas () } bananas .`);

// ES5 equivalent:

console.log(‘He carries ‘+apples+’ apples, ‘+pears+’ pears, and
‘+bananas()+’ bananas.’);

运行代码%20%7B%20return%203%3B%20%7D%0A%0Aconsole.log(%60This%20is%20%24%7Bname%7D.%60)%3B%0A%0Aconsole.log(%60He%20carries%20%24%7Bapples%7D%20apples%2C%20%24%7Bpears%7D%20pears%2C%20and%20%24%7Bbananas()%7D%20bananas.%60)%3B%0A)

以上形式对比 ES5
仅仅是便于字符串拼接。事实上,模板字符串还可以用于多行字符串。记住空格也是字符串的一部分。

let x = `1…

2…

3lines long!`;  // Yay

// ES5 equivalents:

var x = “1…n”+

“2…n”+

“3 lines long!”;

var x = “1…n2…n3 lines long!”;

运行代码

数组

Array对象增加了一些新的静态方法,Array原型上也增加了一些新方法。

首先,Array.from从类数组和可遍历对象中创建Array的实例。类数组对象示例包括:

函数中的arguments;

由document.getElementByTagName()返回的nodeList;

新增加的Map和Set数据结构。

let itemElements = document.querySelectorAll(‘.items’);

let items=Array.from(itemElements);

items.forEach( function (element){

console.log(element.nodeType)

});

// A workaround often used in ES5:

let items=Array.prototype.slice.call(itemElements);

运行代码%3B%0Alet%20items%20%3D%20Array.from(itemElements)%3B%0Aitems.forEach(function(element)%20%7B%0A%20%20%20%20console.log(element.nodeType)%0A%7D)%3B%0A)

在上面的例子中,可以看到items数组拥有forEach方法,该方法是itemElements集合所不具备的。

Array.from的一个有趣的特性是它的第二个可选参数mapFunction。该参数允许你通过一次单独调用创建一个新的映射数组。

let navElements=document.querySelectorAll(‘nav li’);

let navTitles=Array.from(navElements,el=>el.textContent);

运行代码%3B%0Alet%20navTitles%20%3D%20Array.from(navElements%2C%20el%20%3D%3E%20el.textContent)%3B%0Aconsole.log(navTitles.join(”))%3B)

然后,我们可以使用Array.of方法,该方法的表现很像Array构造函数。它适合只传递一个参数的情况。因此Array.of是new
Array()的更优选择。然而,更多的情况下,你会想使用数组字面量。

let x=newArray(3);// [undefined, undefined, undefined]

let y=Array.of(8);// [8]

let z=[1,2,3];// Array literal

最后但同样重要的,有几个方法被添加到Array的原型上。我想find方法将会很受
Javascript 开发者欢迎。

find返回回调返回true的第一个元素。

findIndex返回回调函数返回true的第一个元素的下标。

fill用所给参数“覆盖”数组的元素。

[5,1,10,8].find(n=>n===10)// 10

[5,1,10,8].findIndex(n=>n===10)// 2

[0,0,0].fill(7)// [7, 7, 7]

[0,0,0,0,0].fill(7,1,3)// [0, 7, 7, 7, 0]

Math

Math对象新增了几个方法。

Math.sign返回数字的符号,结果为1、-1或0。

Math.trunc返回无小数位的数字。

Math.cbrt返回数字的立方根。

Math.sign(5);// 1

Math.sign(-9);// -1

Math.trunc(5.9);// 5

Math.trunc(5.123);// 5

Math.cbrt(64);// 4

如果你想学习更多ES6 中的 number 和 math
新特性,
Dr. Axel Rauschmayer将为你解答。

扩展操作符

扩展操作符(…)这个语法用于特定地方扩展元素非常方便,例如函数调用中的参数。让你了解它们用途的最好方法就是举例子了。

首先,我们看看如何在一个另数组中扩展一个数组的元素。

let values=[1,2,4];

let some=[…values,8];// [1, 2, 4, 8]

let more=[…values,8,…values];// [1, 2, 4, 8, 1, 2, 4]

// ES5 equivalent:

let values=[1,2,4];

// Iterate, push, sweat, repeat…

// Iterate, push, sweat, repeat…

运行代码%3B%0Aconsole.log(more)%3B)

扩展语法在传参数调用函数时也非常有用:

let values=[1,2,4];

doSomething(…values);

functiondoSomething(x,y,z){

// x = 1, y = 2, z = 4

}

// ES5 equivalent:

doSomething.apply(null,values);

运行代码%3B%0Afunction%20doSomething(x%2C%20y%2C%20z)%20%7B%0A%20%20%20%20console.log(arguments)%3B%0A%7D%0A)

正如你所看到的,该语法让我们免去通常使用fn.apply()的麻烦。它非常灵活,因为扩展操作符可以用在参数列表中的任意位置。这意味着以下调用方式会产生一样的结果:

let values=[2,4];

doSomething(1,…values);

运行代码%3B%0Afunction%20doSomething(x%2C%20y%2C%20z)%20%7B%0A%20%20%20%20console.log(arguments)%3B%0A%7D)

我们已经把扩展操作符应用在数组和参数中。事实上,它可以用在所有的可遍历对象中,例如一个Node
List:

let form=document.querySelector(‘#my-form’),

inputs=form.querySelectorAll(‘input’),

selects=form.querySelectorAll(‘select’);

let all The Things=[form,…inputs,…selects];

运行代码

现在,all The Things是一个包含节点、子节点和子节点的二维数组。

解构

解构提供了一个方便地从对象或数组中提取数据的方法。对于初学者,请看以下数组示例:

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

// ES5 equivalent:

var arr = [1,2];

var x = arr[0];

vary = arr[1];

使用这个语法,可以一次性给多个变量赋值。一个很好的附加用处是可以很简单地交换变量值:

let x = 1,

y = 2;

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

运行代码

解构也可以用于对象。注意对象中必须存在对应的键:

let obj = { x :1 , y : 2 };

let { x , y } = obj;// x = 1, y = 2

运行代码

你也可以使用该机制来修改变量名:

let obj = { x : 1 , y : 2 };

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

另一个有趣的模式是模拟多个返回值:

function doSomething () {

return [ 1 , 2 ]

}

let [x,y] = doSomething() ;// x = 1, y = 2

运行代码%20%7B%0A%20%20%20%20return%20%5B1%2C%202%5D%0A%7D%0A%0Alet%20%5Bx%2C%20y%5D%20%3D%20doSomething()%3B)

解构可以用来为参数对象赋默认值。通过对象字面量,可以模拟命名参数:

function doSomething ( { y = 1, z = 0 } ){

console.log (y , z);

}

doSomething ( { y : 2 } );

参数

默认值

在 ES6 中,可以定义函数的参数默认值。语法如下:

function doSomething (x, y = 2){

return x * y;

}

doSomething (5) ;// 10

doSomething (5,undefined); // 10

doSomething (5,3) ; // 15

运行代码%20%7B%0A%20%20%20%20return%20x%20*%20y%3B%0A%7D%0A%0Aconsole.log(doSomething(5))%3B%0Aconsole.log(doSomething(5%2C%20undefined))%3B%0Aconsole.log(doSomething(5%2C%203))%3B)

看起来很简洁,对吧? 我肯定你之前在 ES5 中曾经需要给某些参数赋默认值:

function doSomething (x,y){

y = y === undefined ? 2 : y;

return x * y;

}

传递undefined或不传参数时都会触发参数使用默认值。

REST参数

我们已经学习了省略号操作符。剩余参数和它很类似。它同样是使用…语法,允许你把末尾的参数保存在数组中:

function doSomething (x , …remaining){

return x * remaining.length;

}

doSomething (5,0,0,0);// 15

运行代码%20%7B%0A%20%20%20%20return%20x%20*%20remaining.length%3B%0A%7D%0A%0AdoSomething(5%2C%200%2C%200%2C%200)%3B)

模块

模块当然是一个受欢迎的 Javascript
语言新功能。我想仅仅是这个主要特性就值得我们投入到 ES6 中来。

当前任何重要的 Javascript 项目都使用某种模块系统 ——
可能是“展示模块模式”或其他 AMD 或 CommonJS
扩展形式的东西。然而,浏览器并没有任何模块系统特性。为了实现 AMD 或
CommonJS,你通常需要一个构建步骤或加载器。解决这个问题的工具包括
RequireJS、Browserify 和 WebPack。

ES6
规范包含模块化的新语法和加载器。如果你未来想使用模块,应该使用这个语法。现代构建工具支持这种形式(可能通过插件),所以你可以放心使用。(不用担心
—— 我们将在后面的“转译”章节中讨论)

在 ES6
的模块语法中。模块设计围绕export和import关键词。现在让我们看一个包含两个模块的例子:

// lib/math.js

export function sum(x,y){

return x+y;

}

export var pi=3.141593;

// app.js

import{sum, pi}from “lib/math”;

console.log(‘2π = ‘+sum(pi, pi));

正如你所见,可以存在多个export声明。每一个都要明确地指明输出值的类型(本例中的function和var)。

本例中的import声明使用一种语法(类似解构)来明确定义被导入的内容。可以使用*通配符,结合as关键词给模块提供一个本地名称,把模块当成一个整体导入。

// app.js

import * as math from”lib/math”;

console.log(‘2π = ‘+math.sum(math.pi, math.pi));

模块系统有一个default输出。它可以是一个函数。只需要提供一个本地名称就可以导入这个默认值(即无解构):

// lib/my-fn.js

export default function(){

console.log(‘echo echo’);

}

// app.js

import doSomething from ‘lib/my-fn’;

doSomething();

请注意import声明是同步的,但是模块代码需在所有依赖加载完后才会运行。

类是 ES6 中备受热议的一个特性。一部分人认为它不符合 Javascript
的原型特性,另一部分人认为类可以降低从其他语言转过来的入门门槛,并帮助人们构建大规模应用。不管怎样,它是
ES6 的一部分。这里我们快速介绍一下。

类的创建围绕class和constructor关键词。以下是个简短的示例:

class Vehicle{

constructor(name){

this.name = name;

this.kind = ‘vehicle’;

}

getName(){

return this.name;

}

}

// Create an instance

let myVehicle = new Vehicle(‘rocky’);

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.name%20%3D%20name%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20’vehicle’%3B%0A%20%20%20%20%7D%0A%20%20%20%20getName()%20%7B%0A%20%20%20%20%20%20%20%20return%20this.name%3B%0A%20%20%20%20%7D%20%20%20%0A%7D%0A%0Alet%20myVehicle%20%3D%20new%20Vehicle(‘rocky’)%3B%0A%0Aconsole.log(myVehicle.getName())%3B%0A)

注意类的定义不是一般的对象,因此,类的成员间没有逗号。

创造一个类的对象时,需要使用new关键词。继承一个基类时,使用extends:

class Car extends Vehicle{

constructor(name){

super(name);

this.kind=’car’

}

}

let myCar = newCar(‘bumpy’);

myCar instanceof Car;// true

myCar instanceof Vehicle;//true

运行代码%20%7B%0A%20%20%20%20%20%20%20%20this.name%20%3D%20name%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20’vehicle’%3B%0A%20%20%20%20%7D%0A%20%20%20%20getName()%20%7B%0A%20%20%20%20%20%20%20%20return%20this.name%3B%0A%20%20%20%20%7D%20%20%20%0A%7D%0A%0Aclass%20Car%20extends%20Vehicle%20%7B%0A%20%20%20%20constructor(name)%20%7B%0A%20%20%20%20%20%20%20%20super(name)%3B%0A%20%20%20%20%20%20%20%20this.kind%20%3D%20’car’%0A%20%20%20%20%7D%0A%7D%0A%0Alet%20myCar%20%3D%20new%20Car(‘bumpy’)%3B%0A%0Aconsole.log(myCar.getName())%3B%0Aconsole.log(myCar%20instanceof%20Car)%3B%0Aconsole.log(myCar%20instanceof%20Vehicle)%3B)

从衍生类中,你可以使用从任何构造函数或方法中使用super来获取它的基类:

使用super()调用父类构造函数。

调用其它成员,举个例子,使用super.getName()。

还有更多关于类的内容。如果你想深入了解,我推荐 Dr.Axel Rauschmayer
的《Classes in ECAMScript
6》

记号

记号是一个新的原生数据类型,像Number和String一样。你可以使用记号为对象属性创建唯一标识或创建唯一的常量。

const MY_CONSTANT = Symbol();

let obj = {};

obj[MY_CONSTANT] = 1;

注意通过记号产生的键值对不能通过Object.getOwnPropertyNames()获得,在for…in遍历、Object.keys()、JSON.stringify()中均不可见。这是与基于字符串的键相反的。你可以通过Object.getOwnPropertySymbols()获取一个对象的记号数组。

记号与const配合很合适,因为它们都有不可改变的特性。

const CHINESE = Symbol();

const ENGLISH = Symbol();

const SPANISH = Symbol();

switch(language){

case CHINESE:

break;

case ENGLISH:

//

break;

case SPANISH:

//

break;

default:

//

break;

}

你可以为 symbol 添加描述。虽然不可以通过描述获取
symbol,但是可用于代码调试。

const CONST_1 = Symbol(‘my symbol’);

const CONST_2 = Symbol(‘my symbol’);

typeofCONST_1 === ‘symbol’;// true

CONST_1 === CONST_2;// false

想学习更多关于 symbols 的内容吗?Mozilla
开发者网络有一个关于该新的symbol
primitive的文章。

转译

我们现在可以用 ES6 来写代码了。正如介绍中提到的,浏览器对 ES6
特性的支持尚不广泛,且各浏览器也各不相同。很有可能你写的的代码在用户的浏览器中不能完全解析。这就是我们为什么需要把代码转换成能在当前的任何浏览器中良好运行的旧版本
Javascript(ES5)
。这种转换通常称为“转译”。我们必须在应用中这么做,直到所有我们想兼容的浏览器都能运行
ES6 为止。

入门

转译代码并不难。你可以通过命令行直接转译代码,也可以把它作为 Grunt 或
Gulp 之类的任务管理器的插件包含进来。有很多转译解决方案,包括
Babel,Traceur 和 TypeScript。例如, 通过 Babel(之前的 “6to5”)开始使用
ES6
的多种方式。大多数
ES6 特性供你自由使用。

既然你对 ES6
充满热情和期待,为什么不开始使用它呢。根据你想使用的特性和需要兼容的浏览器或环境(比如
Node.js),你可能需要在工作流中引入转译器。如果你确定要使用它们,文件监听器和浏览器动态刷新器可以使你的编码体验更加流畅。

如果你是从零开始,你可能只想通过命令行转译代码(可以从Babel CLI
documentation查看示例)。如果你已经使用任务运行器,如
Grunt 或
Gulp,你可以添加类似gulp-babel或Webpack
babel-loader的插件。对于
Grunt,可使用grunt-babel和很多其他ES6
相关的插件。Browserify
的用户可能会想看看babelify。

大多数特性可以被转换成兼容 ES5
的代码且开销很小。其他的特性则需要额外处理(可由转译器提供),可能有性能损失。如果想把玩一下
ES6 并查看转译后的代码的样子,可以使用各种交互环境(也就是 REPL):

Traceur:网站REPL

Babel:网站REPL

TypeScript:网站REPL

ScratchJS(Chrome
扩展)

注意 TypeScript 不完全是一个转译器。它是一个类型化的 Javascript
超集,编译成 Javascript 代码。在其它特性中,它支持很多 ES6
特性,很像其他编译器。

所以,我究竟可以用什么?

总的来说,部分 ES6
特性几乎是可以“免费”使用的,比如模块,箭头函数,剩余参数和类。这些特性只需很小的开销就可以被转译成
ES5
。Array、String和Math对象和原型的附加方法(如Array.from()和”it”.startsWith(“you”))需要所谓的“polyfills”。Polyfills
是浏览器未原生支持的功能的临时补充。你可以先加载
profill,然后你的代码就可以在浏览器中运行,仿佛浏览器有这个功能一样。Babel
和 Traceur 都提供这种 polyfills。

可在Kangax 的ES6
兼容性表格中查看转译器和浏览器支持的
ES6 特性的完整概述。在写本文时,最新的浏览器已经支持 55% 到 70% 以上 ES6
特性了,看到这个真是鼓舞人心啊。Microsoft Edge、Google Chrome 和 Mozilla
的 Firefox 已经在这方面相互竞争了,这对 web 技术总体来说是非常好的。

就个人而言,可以轻松地使用模块、箭头函数和剩余参数之类的 ES6
新特性对于我的代码是一个极大的提高,是生产力的解放。既然我已经习惯了写
ES6 代码并转译成 ES5,随着时间的推移,更多的 ES6 的好处将会自然显现。

接下来呢?

一旦你安装了转译器,你可能新从let和箭头函数之类的“小”特性开始使用。记住原本就是用
ES5 写的代码将不会被转译器转译。当你使用 ES6
来提高你的代码,并且喜欢它时,你可以逐渐往你的代码中添加更多的 ES6
特性。或者把部分代码转换成新模块或类语法。我保证这样会很爽!

ES6
的内容比本文中所涉及的多得多。未涉及的特性包括Map、Set、标签模板字符串、生成器、Proxy和Promise。让我知道你是否希望下篇文章涉及这些特性。不管怎样,我很高兴推荐
Axel Rauschmayer 博士写的覆盖所有 ES6 特性的《Exploring
ES6》供深入研究。

结语

当浏览器不断添加新特性时, 通过使用转译器,你的所有代码被有效地“锁定”到
ES5 。所以,就算浏览器完全支持了某一个 ES6 特性,兼容 ES5
的版本将被使用,可能会性能更差。你可以指望在某个点(在当时你需要兼容的浏览器和环境)上所有的
ES6 特性最终被支持。到那时,我们需要管理它们,选择性地防止 ES6
特性转译成
ES5,以减少性能开销。考虑这个因素,判断当前是否是开始使用(部分)ES6
的时候。部分公司认为是这样。

发表评论

电子邮件地址不会被公开。 必填项已用*标注