ES6简述(持续创新…)

前言

ES6,全称ECMAScript 6,是ECMA委员会于
2015年6月标准揭晓的新ECMAScript标准。所以还要称ECMAScript
2015,也就是说,ES6即使是ES2015。至今各大浏览器厂商所开发的 JavaScript
引擎都还无形成对 ES2015
中享有特性的到支持,于是乎如 babel、Traceur 等编译器便冒出了。它们能拿从未获得支持之
ES2015 特性转换为 ES5 标准的代码,使该获得浏览器的支持。其中,babel
因该模块化转换器(Transformer)的统筹特征得到了绝大部客 JavaScript
开发者的偏重。

一、变化

一言以蔽之:ES2015 标准供了好多新的语法和编程特性以增长 JavaScript
的开支效率与经验。

次、新的语法

1、let、const

她俩是继 var 之后,新的变量定义方法。

const 更爱被清楚:const 也不怕是 constant 的缩写,跟 C/C++
等经典语言同样,用于定义常量,即不可变量。

ES5单出全局作用域和函数作用域,没有块级作用域,这带来多勿成立之气象。第一种植现象就是是公本见到底内层变量覆盖外层变量。而let则实在为JavaScript新增了块级作用域。用其所声明的变量,只在let令所在的代码块内立竿见影。

2、箭头函数 (arrow function)

以此可能是ES6极度极致常用的一个新特色了,用它们来形容function比原的写法要精简清晰很多:

function(i){ return i + 1; } //ES5
(i) => i + 1 //ES6

苟方程比较复杂,则用因此{}把代码包起来:

function(x, y) { 
    x++;
    y--;
    return x + y;
}
(x, y) => {x++; y--; return x+y}

除开看上去更简单以外,arrow function还有平等桩超级无敌的功能!
长期以来,JavaScript语言的this目标一直是一个叫人讨厌的问题,在对象方法吃使用this,必须十分小心。例如:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout(function(){
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}

 var animal = new Animal()
 animal.says('hi')  //undefined says hi

运转方面的代码会报错,这是坐setTimeout中的this针对的是大局对象。所以为了让她能科学的运作,传统的解决方式有三三两两种:

(1)第一种植是拿this传于self,再就此self来指代this

says(say){
   var self = this;
   setTimeout(function(){
     console.log(self.type + ' says ' + say)
}, 1000);

(2)第二种植方法是故bind(this),即:

says(say){
   setTimeout(function(){
     console.log(self.type + ' says ' + say)
}.bind(this), 1000);

不过本我们来矣箭头函数,就不需要如此辛苦了:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout( () => {
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}
 var animal = new Animal()
 animal.says('hi')  //animal says hi

当我们应用箭头函数时,函数体内的this对象,就是概念时所于的靶子,而非是运用时所当的目标。
并无是因箭头函数内部有绑定this的编制,实际原因是箭头函数根本无团结的this,它的this是继往开来外面的,因此内部的this就是外围代码块的this。

3、模板字符串(template string)

此事物吗是大有因此,当我们而插入大段的html内容及文档中常常,传统的写法非常累,所以之前我们普通会引用一些模板工具库。

可事先看下面一段子代码:

$("#result").append(
  "There are <b>" + basket.count + "</b> " +
  "items in your basket, " +
  "<em>" + basket.onSale +
  "</em> are on sale!"
);

咱若用同积的’+’号来连接文本和变量,而下ES6的新特征模板字符串“继,我们得以直接这么来写:

$("#result").append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

之所以反引号(`)来标识起始,用${}来引用变量,而且具备的空格和缩进都见面叫封存在出口之中。

4、对象字面量扩展语法

4.1 方法属性省略 function

// es5
function bar() {
  return 'bar'
},

// es6
bar() {
  return 'bar'
}

4.2 支持 __proto__ 注入

当 ES2015
中,我们好于一个目标硬生生的施其 __proto__,这样它就是足以成为这值所属类的一个实例了。

class Foo {
  constructor() {
    this.pingMsg = 'pong'
  }

  ping() {
    console.log(this.pingMsg)
  }
}

let o = {
  __proto__: new Foo()
}

o.ping() //=> pong

产生啊用吧?当自身眷恋扩大或者覆盖一个像样的不二法门,并扭转一个实例,但当另外定义一个接近即感觉浪费了。那自己得如此做:

let o = {
  __proto__: new Foo(),

  constructor() {
    this.pingMsg = 'alive'
  },

  msg: 'bang',
  yell() {
    console.log(this.msg)
  }
}

o.yell() //=> bang
o.ping() //=> alive

4.3 同名方法属性省略语法

呢是看上去有点鸡肋的初特点,不过当召开 JavaScript
模块化工程的时段则发矣用武之地。

// module.js
export default {
  someMethod
}

function someMethod() {
  // ...
}

// app.js
import Module from './module'

Module.someMethod()

4.4 可以动态计算的性质名称(这个我觉得要蛮有效的)

let arr = [1, 2, 3]
let outArr = arr.map(n => {
  return {
    [ n ]: n,
    [ `${n}^2` ]: Math.pow(n, 2)
  }
})
console.dir(outArr) //=>
  [
    { '1': 1, '1^2': 1 },
    { '2': 2, '2^2': 4 },
    { '3': 3, '3^2': 9 }
  ]

5、表达式解构(destructuring)

立即是es6相当有效之一个表征。

ES6同意以一定模式,从数组和目标吃领取值,对变量进行赋值,这给名解构(Destructuring)。

例:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

故而ES6全好像下这么写:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat, dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

掉可以这样形容:

let dog = {type: 'animal', many: 2}
let { type, many} = dog
console.log(type, many)   //animal 2

 6、默认参数(default)和累参数(rest)

6.1 默认参数

调用animal()方式时忘了污染参数,传统的做法就是添加这无异句子type = type || 'cat'来因定默认值。

function animal(type){
    type = type || 'cat'  
    console.log(type)
}
animal()

要用ES6咱得一直这么写:

function animal(type = 'cat'){
    console.log(type)
}
animal()

6.2 后续参数

咱俩了解,函数的 call 和 apply 在以及之最好老差距就是一个每当首参数后传出各个参数,一个凡在首参数后传出一个暗含有参数的数组。

倘若我们在贯彻某些函数或措施时,也盼实现像 call 一样的用方法,在ES5遭我们得用arguments

function fetchSomethings() {
  var args = [].slice.apply(arguments)

  // ...
}
function doSomeOthers(name) {
  var args = [].slice.apply(arguments, 1)

  // ...
}

倘以 ES6 中,我们好十分粗略的采取 … 语法糖来实现:

function fetchSomethings(...args) {
  // ...
}
function doSomeOthers(name, ...args) {
  // ...
}

若留意的是,...args 后不可再增长。

则于言语角度看,arguments 和 ...args 是可以以采取
,但发生一个新鲜情形则不得:arguments 在箭头函数中,会跟随上下文绑定到上层,所以在匪确定上下文绑定结果的状态下,尽可能不要再次箭头函数吃又下 arguments,而使用 ...args

注意事项

默认参数值连续参数需要以顺序原则,否则会错。

function(...args, last = 1) {
  // This will go wrong
}

老三、新的数据类型

于 ES5 中,JavaScript 中着力的数据类型:

  • String 字符串
  • Number 数字(包含整型和浮点型)
  • Boolean 布尔值
  • Object 对象
  • Array 数组

内部以分为值类型引用类型,Array 其实是 Object 的均等种子类。

1、Set(集) 和 WeakSet(弱集)

高中数学中,集不可知包含相同的因素。

let s = new Set()
s.add('hello').add('world').add('hello')
console.log(s.size) //=> 2
console.log(s.has('hello')) //=> true

当实际上付出被,我们出众多索要因此到集结的场面,如搜寻、索引建立等。

WeakSet 在 JavaScript
底层作出调整(在非降级兼容的情事下),检查元素的变量引用情况。如果元素的援已为整个解除,则该因素即会见吃去除,以省内存空间。这意味著无法直接参加数字还是字符串。另外
WeakSet 对素来严格要求,必须是
Object,当然矣,你为堪用 new String('...') 等花样处理元素。

let weaks = new WeakSet()
weaks.add("hello") //=> Error
weaks.add(3.1415) //=> Error

let foo = new String("bar")
let pi = new Number(3.1415)
weaks.add(foo)
weaks.add(pi)
weaks.has(foo) //=> true
foo = null
weaks.has(foo) //=> false

2、Map 和 WeakMap

自从数据结构的角度来说,映射(Map)跟原先的 Object 非常相似,都是
Key/Value 的键值对组织。但是 Object 有一个为人口格外难受的限量:key
必须是字符串或数字。在相似情况下,我们并无会见受到上这同一限,但假如我们得树立一个目标映射表时,这无异于限量显得越发棘手。

若是 Map 则解决了就等同题材,可以下其它对象作为该
key,这好实现从前面无克兑现或难以实现的效力,如在品种逻辑层实现数量索引等。

let map = new Map()
let object = { id: 1 }

map.set(object, 'hello')
map.set('hello', 'world')
map.has(object) //=> true
map.get(object) //=> hello

苟 WeakMap 和 WeakSet 很相近,只不过 WeakMap
的键和值都见面检讨变量引用,只要这个的援全被解除,该键值对即使会见受删去。

let weakm = new WeakMap()
let keyObject = { id: 1 }
let valObject = { score: 100 }

weakm.set(keyObject, valObject)
weakm.get(keyObject) //=> { score: 100 }
keyObject = null
weakm.has(keyObject) //=> false

四、类(Class)

回忆一下于 ES5 中,我们是怎在 JavaScript 中实现类似的?

function Foo() {}
var foo = new Foo()

ES6 中的止是一模一样栽语法糖,用于定义原型(Prototype)的。

1、语法

1.1 定义

class Person {
  constructor(name, gender, age) {
    this.name = name
    this.gender = gender
    this.age = age
  }

  isAdult() {
    return this.age >= 18
  }
}

let me = new Person('Me', 'man', 19)
console.log(me.isAdult()) //=> true

1.2 继承

class Animal {
  say() {
    console.log('say')
  }
}

class Person extends Animal {
  constructor(name, gender, age) {
    super() // must call `super` before using `this` if this class has a superclass

    this.name = name
    this.gender = gender
    this.age = age
  }

  isAdult() {
    return this.age >= 18
  }
}

class Man extends Person {
  constructor(name, age) {
    super(name, 'man', age)
  }
}

let me = new Man('Me', 19)
console.log(me.isAdult()) //=> true
me.say() //=> say

ES6
中若要是一个类继承于另外一个近似设作为那个子类,只需要在子类的名背后长 extends {SuperClass} 即可。

1.3 静态方法

ES6
中之近乎机制支持 static 类型的方式定义,比如说 Man 是一个近似,而我欲也该定义一个 Man.isMan() 方法以用于项目检查,我们得以这样做:

class Man {
  // ...

  static isMan(obj) {
    return obj instanceof Man
  }
}

let me = new Man()
console.log(Man.isMan(me)) //=> true

遗憾之是,ES2015
的切近并无克一直地定义静态成员变量,但假如必须实现此类需求,可以用static 加上 get 语句和 set 语句子实现。

class SyncObject {
  // ...

  static get baseUrl() {
    return 'http://example.com/api/sync'
  }
}

不满和期望

即当前的话,ES6 的接近机制仍很鸡肋:

  1. 非支持个体属性(private
  2. 匪支持前置属性定义,但可用 get 语句和 set 语句子实现
  3. 勿支持多更继承
  4. 从未有过接近于协议(Protocl)或接口(Interface)等之概念

五、生成器(Generator)

Generator
的筹划初衷是为了供平等种植能方便地好成一层层对象的办法,如计量斐波那么契数排列(Fibonacci
Sequence)(俗称兔子数列):

function* fibo() {
  let a = 1
  let b = 1

  yield a
  yield b

  while (true) {
    let next = a + b
    a = b
    b = next
    yield next
  }
}

let generator = fibo()

for (var i = 0; i < 10; i++)
  console.log(generator.next().value) //=> 1 1 2 3 5 8 13 21 34 55

只要您无碰过
Generator,你早晚会指向这段代码感到很意外:为什么 function 后会面发一个 *?为什么函数里以了 while (true)也从没上死循环而造成死机?yield 又是啊破?

1、基本概念

1.1 Generator Function

生成器函数用于转移生成器(Generator),它和常见函数的概念方式的区分就是在于它们用以 function 后加一个 * 。

function* FunctionName() {
  // ...Generator Body
}

生成器函数的宣示形式不是必须的,同样可采取匿名函数的花样:

let FunctionName = function*() { /* ... */ }

生成器函数的函数内容将会晤是针对性诺生成器的运行内容,其中支持一种植新的语法 yield。它的企图和 return 有点相似,但决不退出函数,而是断出生成器运行时

卿得将全副生成器运行时作一长条长面条(while (true) 则就是最最加上之),JavaScript
引擎在各个一样不善遇到 yield 就假设断然一刀,而切面所成的“纹路”则是 yield 出来的价值。

1.2 Generator

生成器在某种意义上可看成为跟 JavaScript
主线程分离的运转时,它可天天让 yield 切回主线程(生成器不影响主线程)。

各国一样糟生成器运行时叫 yield 都可以拉动出一个价值,使其归来主线程中;此后,也足以由主线程返回一个价值回到生成器运行时着:

let inputValue = yield outputValue

生成器切出主线程并带动起 outputValue,主函数经处理后(可以是异步的),把 inputValue 带回生成器中;主线程可以通过 .next(inputValue) 方法返回值到生成器运行时遭遇。

2、基本以方法

2.1 构建生成器函数

行使 Generator
的首先步自然是要构建生成器函数,理清构建思路。拿斐波那契数列作例子:

斐波那契数列的定义:第 n (n ≥ 3) 项是第 n – 1 起和第 n – 2 之和,而第 1
宗与第 2 桩都是 1。

function* fibo() {
  let [a, b] = [1, 1]

  yield a
  yield b

  while (true) {
    [a, b] = [b, a + b]
    yield b
  }
}

如此这般设计生成器函数,就可优先把先设定好之首个别宗输出,然后经过最循环不断把后一致项输出。

2.2  启动生成器

生成器函数不能够一直用来当生成器使用,需要事先采取这函数得到一个生成器,用于运行生成器内容与接收返回值。

let gen = fibo()

2.3 运行生成器内容

博生成器以后,我们尽管可以经过它进行数列项生成了。此处演示获得前 10 起。

let arr = []
for (let i = 0; i < 10; i++)
  arr.push(gen.next().value)

console.log(arr) //=> [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]

 

六、原生的模块化

于ES6之前, 前端就用RequireJS或者seaJS实现模块化,
requireJS是基于AMD规范的模块化库,  而诸如seaJS是依据CMD规范的模块化库,
 两者都是为为加大前端模块化的家伙。

如今ES6打带了模块化, 也是JS第一次于支持module, 在老大老以后
,我们可以一直作用importexport于浏览器中导入和导出各个模块了,
一个js文件表示一个js模块;

当代浏览器对模块(module)支持程度不同, 目前且是利用babelJS,
或者Traceur把ES6代码转化为兼容ES5本子的js代码。

1、ES6的模块化的主导规则或特色:

(1):每一个模块只加载同坏, 每一个JS只实行同样差,
如果下次复失去加载与目录下同文件,直接由外存中读取。
一个模块就是一个单例,或者说就算是一个靶;

(2):每一个模块内声明的变量都是有变量, 不见面传染全局作用域;

(3):模块内部的变量或者函数可以通过export导出;

(4):一个模块可导入别的模块。

//lib.js
//导出常量
export const sqrt = Math.sqrt;
//导出函数
export function square(x) {
    return x * x;
}
//导出函数
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//main.js
import { square, diag } from './lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

2、几种导出方式:

2.1 内联导出**

export class Employee{  
  constructor(id, name, dob){  
    this.id = id;  
    this.name=name;  
    this.dob= dob;  
  }  
  getAge(){  
    return (new Date()).getYear() - this.dob.getYear();  
  }  
}  
export function getEmployee(id, name, dob){  
  return new Employee(id, name, dob);  
}  
var emp = new Employee(1, "Rina", new Date(1987, 1, 22));  

案例被的模块导出了少于个对象:
Employee类,getEmployee函数。因对象emp未让导出,所以该按为模块私出。

2.2 导出同样组对象

于模块的终极单独开展导出声明,以导出该模块中之消导出的目标。

class Employee{  
  constructor(id, name, dob){  
    this.id = id;  
    this.name=name;  
    this.dob= dob;  
  }  
  getAge(){  
    return (new Date()).getYear() - this.dob.getYear();  
  }  
}  
function getEmployee(id, name, dob){  
  return new Employee(id, name, dob);  
}  
var x = new Employee(1, "Rina", new Date(1987, 1, 22));  
export {Employee, getEmployee};  

当导出时,重命名对象啊是可的。如下例所示,Employee在导出时名字改成以Associate,函数GetEmployee改名为getAssociate。

export {  
    Associate as Employee,  
    getAssociate as getEmployee  
};  

2.3 Default导出

采用要字default,可拿对象标注为default对象导出。default关键字于各国一个模块中不得不采取同一潮。它既是可以用于内联导出,也得以用于一组靶导出声明中。

这种导出的法门不需了解变量的名字, 相当于是匿名的,
直接拿开发之接口给export;

要一个js模块文件就单单来一个功效, 那么即便好应用default导出:

function foo(..) {  
// ..  
}  
export default foo;  
// or:
export{ foo as default }; 

3 、导入

3.1 无对象导入

import './module1.js'; 

3.2 导入默认对象

import foo from "foo";  
// or:  
import { default as foo } from "foo";  

3.3 导入命名的靶子

import { foo } from "foo";  

当然为可是于与一个扬言遭导入默认对象以及命名对象。这种情形下,默认对象要定义一个号:

import {default as d, foo} from './module1.js';  

3.4 导入所有目标

import * as allFromModule1 from './module1.js';  

3.5 可编程式的按需导入

比方想根据某些规则或当某事件时有发生后再也加载需要的模块,可经过采取加载模块的而编程API(programmatic
API)来落实。使用System.import方法,可按照次序设定加载模块。这是一个异步的措施,并回Promise。

System.import('./module1.js')  
    .then(function(module1){  
        //use module1  
    }, function(e){  
        //handle error  
    });  

若果模块加载成功都以导出的模块成功传送给回调函数,Promise将见面透过。如果模块名称有误或由网络延迟等由促成模块加载失败,Promise将见面失败。

等会,什么是Promise?

七、Promise**

Promise
是平等栽用于缓解回调函数无限嵌套的工具(当然,这只是是里同样栽),其字面意义吗“保证”。它的意就是是“免去”异步操作的回调函数,保证能通过持续监听而获取返回值,或针对错误处理。它亦可如异步操作变得齐刷刷,也再次好控制。

1、基本用法

如果呢一个函数赋予 Promise 的力,先要创一个 Promise
对象,并拿那个当函数值返回。Promise
构造函数要求传入一个函数,并带有 resolve 和 reject 参数。这是少单用于了
Promise
等待的函数,对应的成功失败。而我们的逻辑代码就于这个函数中展开。

function fetchData() {
    return new Promise((resolve, reject) => {
        if (/* 异步操作成功 */){
          resolve(value);
        } else {
          reject(error);
        }
    });
}

Promise 构造函数接受一个函数作为参数,该函数的鲜只参数分别是 resolve
方法以及 reject 方法。

使异步操作成,则据此 resolve 方法以 Promise
对象的状态,从「未就」变为「成功」(即由 pending 变为 resolved);

假若异步操作失败,则用 reject 方法以 Promise
对象的状态,从「未到位」变为「失败」(即从 pending 变为 rejected)。

基本的 api

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.then()
  4. Promise.prototype.catch()
  5. Promise.all() // 所有的姣好

  6. Promise.race() // 竞速,完成一个即可

2、进阶

promises 的怪异在于给我们先的 return 与 throw,每个 Promise
都见面提供一个 then() 函数,和一个 catch(),实际上是 then(null, …)
函数:

somePromise().then(functoin(){
  // do something
});

咱们得开三桩事:

1. return 另一个 promise
2. return 一个同步的值 (或者 undefined)
3. throw 一个同步异常 ` throw new Eror('');`

 2.1 封装同步和异步代码

new Promise(function (resolve, reject) {
  resolve(someValue);
});

// 写成
Promise.resolve(someValue);

2.2  捕获并异常

new Promise(function (resolve, reject) {
  throw new Error('悲剧了,又出 bug 了');
}).catch(function(err){
  console.log(err);
});

若果是并ECMAScript代码,可以描绘成:

Promise.reject(new Error("什么鬼"));

2.3 多只很捕获,更加精准的捕获

somePromise.then(function() {
  return a.b.c.d();
}).catch(TypeError, function(e) {
//If a is defined, will end up here because
//it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
//Will end up here if a wasn't defined at all
}).catch(function(e) {
//Generic catch-the rest, error wasn't TypeError nor
//ReferenceError
});

2.4 获取两个 Promise 的返回值

2.4.1 .then 方式顺序调用

2.4.``2 设定更高层的作用域

2.4.``3 spread

(未完待续……)