前端开发者应该知道的 ES6 新特性

ECMAScript 2015 即 ECMAScript 6(以下简称 ES6) ,是ECMAScript标准发布的最新版本,于2015年6月批准通过。
ES6 是对语言的一次重大更新,是自从2009年ES5标准化后的第一次重大更新。
主要的JavaScript引擎正在逐步实现这些特性,点击这里查看浏览器的兼容情况。

现在的你还没使用过 ES6 新语法,那么很可能你已经不是一名合格的前端开发者,本文总结了一些使用概率较多的ES6 新特性,希望对你有所帮助。

新的对象属性和方法

在ES6标准中新增了许多新的对象属性和方法,可以看看尼古拉斯的新书《JavaScript面向对象精要》,当然可以去搜索引擎查询一个对象属性的办法,但是那样效率也是太低了。
摘自《DOM启蒙》的方法,用如下方式在Chrome浏览器控制台打印获取Object的所有属性:

Object.getOwnPropertyNames(Object).sort().forEach(function (val) {
console.log(val)
});

上面的代码会有如下的输出:
image

当然用for in也是可以的,执行如下片段打印出Person的所有属性和值:

var Person={
name:'祝仙森',
sex:'男',
age:1000
};
for(var key in Person){
console.log(key+':'+Person[key])
}

箭头函数和静态this(Arrows and Lexical This)

箭头函数使用胖箭头 (=>) 语法,与C#,Java 8和CoffeeScript类似。支持表达式和语句作为函数体。在箭头函数出现之前,每个新定义的函数都有其自己的 this 值(例如,构造函数的 this 指向了一个新的对象;严格模式下的函数的 this 值为 undefined;如果函数是作为对象的方法被调用的,则其 this 指向了那个调用它的对象 )。在面向对象风格的编程中,这被证明是非常恼人的事情。不像普通函数,箭头函数的this是和词法作用域绑定的。

// 表达式作为函数体
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// 语句作为函数体
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});

// 静态this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};

参考文章:ES6 In Depth: Arrow functions || ES6 In Depth: Arrow functions(译)


let 的暂存死区与错误

var声明一个变量,可同时初始化。
let声明一个块级本地变量,可以同时初始化。
const声明一个只读的命名常量
let 允许把变量的作用域限制在块级域中。与 var 不同处是:var 申明变量要么是全局的,要么是函数级的,而无法是块级的。
在同一个函数或同一个作用域中用let重复定义一个变量将引起 TypeError.

if (x) {
let foo;
let foo; // TypeError thrown.
}

然而,在这个语句块中,在变量声明之前引用这个变量会导致一个 ReferenceError的结果, 因为let变量 在”暂存死区” (从块的开始到声明这段).

function do_something() {
console.log(foo); // ReferenceError
let foo = 2;
}

在 ES6 中, let 将会提升这个变量到语句块的顶部,修复了这个问题。


Class 语法糖

以往我们在写 JS 程序的时候,会通过构造函数,去定义并生成对象,下面例子返回一个坐标:

function Point (x, y) {
this.x = x;
this.y = y;
}
Point.prototype.tostring = function () {
return '('+'this.x'+','+'this.y'+')';
}
var p = Point(99, 100);
console.log( p.tostring );// (99,100)

以上写法与传统的 Java 、C++ 语言编写面向对象差异很大,不易弄懂。

ES5中通过原型链模式来实现其他语言中通过类实现的逻辑,这种模式可以看作一种类模式。

而在 ES6 中,引入了Class(类)这个概念,作为对象的模板,通过 class 关键字定义类。
我们将以上代码片段用 ES6 编写:

//定义类
class Point {
//定义构造函数 等同于 function Point (x, y){ ... }
constructor(x, y){
this.x = x;
this.y = y;
}
//定义 toString 方法
toString() {
return '('+'this.x'+','+'this.y'+')';
}

}


ES6中的类(Class)是一个基于面向对象原型链模式简单的语法糖,通过简洁的声明模式使类模式更易用,并鼓励交互操作。
类支持基于原型链的继承super调用实例与静态方法构造函数

Promise 对象

Javascript语言的执行环境是”单线程”(single thread)。
这种模式的坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

ES6 诞生以前,异步编程(Asynchronous)的方法,大概有下面四种:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise 对象

Promise 对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。Promise使用详解,对比其他异步编程方法,请查看阮一峰老师的讲解:Javascript异步编程的4种方法


模板文本 Template Literals

模板文本 也即 模板字符串 ,它使用反引号 (` `) 来代替普通字符串中的用双引号和单引号。
模板字符串 可以包含特定语法(${expression})的占位符,一种更优雅、更方便的方式。

多行字符串

// 「普通字符串」
console.log("第一行\n\第二行");

// 「模板字符串」,直接回车即可
console.log(`第一行
第二行`);

打印结果均是:
// 第一行
// 第二行

表达式插补

// 「普通字符串」
var a = 5;
var b = 10;
console.log("a + b = " + (a + b) + ", \n2 * a + b = " + (2 * a + b) + ".");

// 「模板字符串」
var a = 5;
var b = 10;
console.log(`a + b = ${a + b}, \n2 * a + b = ${2 * a + b}.`);

打印结果均是:
// a + b = 15,
// 2 * a + b = 20.

Markdown 中使用反撇号,加反引号即可。


默认参数 Default parameters

以前我们需要这样来定义默认参数:

var link = function (height, color, url) {
var height = height || 50;
var color = color || 'red';
var url = url || 'https://jandou.com';
...
}

一切工作正常,直到参数值是 0 之后,就有问题了,因为在 JavaScript 中,0 表示fasle,它是默认被 hard-coded 的值,而不能变成参数本身的值。
当然,如果你非要用 0 作为值,我们可以忽略这一缺陷并且使用逻辑或就行了!但在ES6中借鉴了 Ruby 语法,直接把默认值放在函数申明里,你只需要这样:

var link = function(height = 50, color = 'red', url = 'https://jandou.com') {
...
}

解构赋值

解构赋值是语法一个Javascript表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。

一般用法:

let a, b, rest;

/* array 解构赋值 */
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

[a, b, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(b); // 2
console.log(rest); // [3, 4, 5]

/* Object 解构赋值 */
({a, b} = {a:1, b:2});
console.log(a); // 1
console.log(b); // 2

/* object & ...rest 解构赋值 */
({a, b, ...rest} = {a:1, b:2, c:3, d:4});
// {a: 1, b: 2, c: 3, d: 4}

rest;
// {c: 3, d: 4}
注意: …rest 必须是解构赋值参数列表的最后一个参数!

ES6 Module 加载模块

解构赋值可以帮助加载一个模块的特定子集,像Add-on SDK中:

// 文件 ./urls.js
export const stockfasturl = `https://developer.mozilla.org/webservice/fastview/stock/`;

export const FS = {
stockfasturl,
};

export const urls = {
FS
};

export default urls;
// 文件 ./app.js
import {urls} from `./urls.js`;

const sf_test = `${urls.FS.stockfasturl}/stockfast${db_id}/${stock_uid}.SH`;

解构嵌套对象和数组

var metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localization_tags: [ ],
last_edit: "2014-04-14T08:43:37",
url: "/de/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung"
}
],
url: "/en-US/docs/Tools/Scratchpad"
};

var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"

参考链接:
1、ECMAScript 6 入门 from 阮一峰
2、学习ES2015:ES2015特性的详细概述,基于LUKE HOBAN《ES6特性》[译]
3、Learn ES2015 babeljs.io
4、模板字符串 from MDN web docs
5、深入浅出ES6(四):模板字符串 from InfoQ
6、解构赋值 from MDN web docs

坚持原创技术分享,您的支持将鼓励我继续创作!