1

简介

  • ECMAScript(ECMA或ES) 是规范, JavaScript的是其规范的实现
  • ECMAScript 2015 = ES2015 ≈ ES6

ECMAScript 的历史

时间 ECMA JS 解释
1996.11 ES 1.0 JS稳定 Netscape将JS提交给ECMA组织,ES正式出现
1998.06 ES 2.0 ES2正式发布
1999.12 ES 3.0 ES3被广泛支持
2007.10 ES 4.0 ES4过于激进,被废了
2008.07 ES 3.1 4.0退化为严重缩水版的3.1<br/>因为吵得太厉害,所以ES 3.1代号为Harmony(和谐)
2009.12 ES 5.0 ES 5.0正式发布<br/>同时公布了JavaScript.next也就是后来的ES 6.0
2011.06 ES 5.1 ES 5.1成为了ISO国际标准
2013.03 ES 6.0 ES 6.0草案定稿
2013.12 ES 6.0 ES 6.0草案发布
2015.06 ES 6.0 ES 6.0预计发布正式版<br/>JavaScript.next开始指向ES 7.0

语法提案批准流程

Stage 0 - Strawman(展示阶段)
Stage 1 - Proposal(征求意见阶段)
Stage 2 - Draft(草案阶段)
Stage 3 - Candidate(候选人阶段)
Stage 4 - Finished(定案阶段)

由TC39 委员会批准

Let、const

  • 块级作用域
  • 不存在变量提升

暂时性死区(temporal dead zone,简称 TDZ):在代码块内,声明变量之前,该变量都是不可用的。

  • 相同作用域内,不允许重复声明
  • 区别:

    • let声明变量
    • const声明只读常量,常量指向的那个内存地址不得改动,且一旦声明变量,就必须立即初始化。

解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
适用于数组(这里泛指具有Iterator接口的数据结构)、对象、字符串、数值和布尔值,可设置默认值。

//数组
let [x, y = 'b', z='c'] = ['a', undefined, null];// x='a', y='b' z=null
//对象
let { bar, foo } = { foo: "aaa", bar: "bbb" };//foo = "aaa"; bar = "bbb"
//字符串,先转为数组
const [a, b, length : len] = 'ho';//a="h";b= "o";len = 2
//数值与布尔值,先转为对象
let {toString: s} = 123;//s = Number.prototype.toString
let {toString: s} = true;//s = Boolean.prototype.toString
  • 如果解构不成功,变量的值就等于undefined
  • 只有当成员严格等于undefined,默认值才会生效
  • 两边的结构要一致
  • 结构完立即赋值,结构和赋值不能分开进行
  • 使用于对象时,键对键,值对值;且大括号不能在行首(会理解成语法块),可用小括号包裹
  • 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象

字符串扩展

查找

  • includes(String):是否找到参数字符串。
  • startsWith(String):是否以参数字符串开头。
  • endsWith(String):是否以参数字符串结尾。
let s = 'Hello world!';
s.includes('o') // true
//均可接受第二个位置参数

重复

  • repeat(Number)
'x'.repeat(3) // "xxx"

模板字符串

  • 支持变量、表达式、函数调用,允许嵌套
  • 反引号需使用反斜杠转义
  • trim消除空格和换行
$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);
//trim
$('#list').html(`
<ul>
  <li>first</li>
  <li>second</li>
</ul>
`.trim());

数值的扩展

  • Number.isFinite()-只对数值返回true
  • Number.isNaN()-只对NaN返回true
  • Number.isInteger()-只对整数返回true
  • Number.isSafeInteger()-只对安全整数(-2^53到2^53,不含端点)返回true
  • Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER安全整数的上下限常量
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false

Math对象扩展:http://es6.ruanyifeng.com/#do...


函数的扩展

  • 参数默认值,建议用于尾参数,不可在函数内再次声明
function log(x, y = 'World') {}
  • length:从第一个参数到首个指定默认值参数间参数的个数
(function (a, b = 1, c) {}).length // 1
  • rest:获取函数的多余参数,须用于尾参数
function add(...values) {}
add(2, 5, 3)
  • name:函数的名字
function f() {}
//或者
var f = function () {};
f.name // "f"

箭头函数

  • 只有一个参数,可省略参数的小括号,无参数时不可省略
  • 如果只有一条语句且无return,可以省略语句块的大括号
  • 如果只有一条语句且有return,可以省略大括号和return,仅有return(即return后无参数)时不可省略
  • 直接返回对象,须使用小括号包裹
let getTempItem = id => ({ id: id, name: "Temp" });
  • 可以使用解构赋值,可以嵌套
const full = ({ first, last }) => first + ' ' + last;
const pipeline = (...funcs) =>
  val => funcs.reduce((a, b) => b(a), val);
  • 注意:

    • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

数组的扩展

扩展运算符

用于数组赋值须放在参数的最后一位。

console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5
//复制数组
const a1 = [1, 2];
const a2 = [...a1];// 写法一
const [...a2] = a1;// 写法二
//合并数组
const a2 = [3, 4];
[...a1, ...a2] //[1,2,3,4]
//配合解构赋值
const [first, ...rest] = [1, 2, 3, 4, 5];// first= 1;rest= [2, 3, 4, 5]
//字符串
[...'hello'] // [ "h", "e", "l", "l", "o" ]

新增方法

  • Array.from():将数组对象(有length属性)或可遍历的对象(包括 ES6 新增的数据结构 Set 和 Map)转为真正的数组,接受第二个参数(对每个元素进行处理,处理值返回数组)。
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
  • Array.of():将一组值转换为数组。
Array.of(3, 11, 8) // [3,11,8]
  • copyWithin(target[必需,替换开始位置,负值为倒数], start = 0[可选,读取开始位置,负值为倒数], end = this.length[可选,读取结束位置,负值为倒数]):在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
  • find(fn)findIndex(fn):找出第一个符合条件的数组成员。如果无符合条件的成员,返回undefined。
//find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10
  • fill(text[必需,填充内容],start = 0[可选,开始位置,负值为倒数], end = this.length[可选,结束位置,负值为倒数]): 使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']
  • includes(text[必需,内容],start = 0[可选,搜索开始位置,负值为倒数], end = this.length[可选,搜索结束位置,负值为倒数]):搜索数组是否包含给定的值。
[1, 2, 3].includes(3, -1); // true

对象的扩展

  • 简洁表示:对象的key和value一样时,可以省略key,方法可省略function
const foo = 'bar';
const baz = {
    foo,
   method() {
    return "Hello!";
  }
}
  • 属性名表达式: 不能使用简洁表示
let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
}
  • Object.is(): 判断两值是否相等
//和===的区别
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
  • Object.assign(target[目标对象], source1, source2,...): 将源对象所有可枚举属性复制合并到目标对象。
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
  • Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
  • 遇到同名属性,Object.assign的处理方法是后面替换前面的。
  • Object.assign可以用来处理数组,但是会把数组视为对象。
  • Object.assign只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。

Set和Map数据结构

Set(集合)

集合:集合是由一组无序且唯一(即不能重复)的项组成的,可以想象成集合是一个既没有重复元素,也没有顺序概念的数组

基本用法

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成 Set 数据结构。

const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {
  console.log(i);
}
// 2 3 5 4
// 去除数组的重复成员
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]

向 Set 加入值的时候,不会发生类型转换。

属性和方法

Set 结构的实例有以下属性。

  • size:返回Set实例的成员总数。
实例操作方法(用于操作数据)
  • add(value):添加某个值,返回 Set 结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。
s.add(1).add(2).add(2);
// 注意2被加入了两次

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2);
s.has(2) // false
遍历方法(用于遍历成员)

Set 结构的实例有四个遍历方法,可以用于遍历成员。

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员

需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值;

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存。

语法

WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}
方法

WeakSet 结构有以下三个方法。

  • add(value):向 WeakSet 实例添加一个新成员。
  • delete(value):清除 WeakSet 实例的指定成员。
  • has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

Map(字典)

类似对象,唯一的区别是key的范围不限于字符串,各种类型的值(包括对象)都可以当作键。主要用于数据存储

const map = new Map([
  ['name', '张三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
属性和方法
  • size:属性返回 Map 结构的成员总数。
  • set(key, value):

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

const m = new Map();

m.set('edition', 6)        // 键是字符串
m.set(262, 'standard')     // 键是数值
m.set(undefined, 'nah')    // 键是 undefined
//或者
let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');
  • get(key):

get方法读取key对应的键值,如果找不到key,返回undefined。

  • has(key):

has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中

  • delete(key):

delete方法删除某个键,返回true。如果删除失败,返回false。

  • clear():

clear方法清除所有成员,没有返回值。

遍历:

提供三个遍历器生成函数和一个遍历方法。Map 的遍历顺序就是插入顺序

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历 Map 的所有成员。
数据结构转换
  • Map 转为数组
const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
  • 数组 转为 Map
new Map([
  [true, 7],
  [{foo: 3}, ['abc']]
])
// Map {
//   true => 7,
//   Object {foo: 3} => ['abc']
// }
  • Map 转为对象
function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }
  • 对象转为 Map
function objToStrMap(obj) {
  let strMap = new Map();
  for (let k of Object.keys(obj)) {
    strMap.set(k, obj[k]);
  }
  return strMap;
}

objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}
  • Map 转为 JSON

Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。

function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'

另一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON。

function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
  • JSON 转为 Map

JSON 转为 Map,正常情况下,所有键名都是字符串。

function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}

但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是数组转为 JSON 的逆操作。

function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}

WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。
区别:

  • WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  • WeakMap的键名所指向的对象,不计入垃圾回收机制。

区别

集合又和字典有什么区别呢:

  • 共同点:集合、字典可以存储不重复的值
  • 不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储

Promise对象

Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件),更合理和更强大。

  • 特点:

    • 对象的状态[pending(进行中)、fulfilled(已成功)和rejected(已失败)]不受外界影响
    • 一旦状态改变,就不会再变,任何时候得到的都是这个结果
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value); //将Promise对象的状态从“未完成”变为“成功”,并将异步操作的结果,作为参数传递出去
  } else {
    reject(error); //将Promise对象的状态从“未完成”变为“失败”,并将异步操作报出的错误,作为参数传递出去
  }
});
//调用
promise.then(value=> {
  // success
}, error=> { //可选
  // failure
});
//或者
promise.then(value => {
  // success
}).catch(error => {
  // failure
});
  • Promise.all([p1, p2, ...]): 将多个 Promise 包装成一个新的 Promise ,状态和值取决于里面的实例(fulfilled:需均成功,返回包含每个实例返回值的数组;rejected:至少一个失败,返回第一个失败的实例返回值)
  • Promise.race([p1, p2, ...]): 将多个 Promise 包装成一个新的 Promise,状态和参数均由里面第一个改变的状态的实例决定
  • Promise.resolve():简写,将现有对象转为 状态为fulfilled的Promise 对象。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  • Promise.reject():简写,将现有对象转为 状态为rejected的Promise 对象。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

Iterator(遍历器)

概念

数据集合:主要是Array,Object,Map,Set。
遍历器(Iterator)为上述数据集合提供了统一的访问机制。

Iterator 的作用有三个:
  • 一是为各种数据结构,提供一个统一的、简便的访问接口;
  • 二是使得数据结构的成员能够按某种次序排列;
  • 三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

for...of 循环

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}
区别:
  • for...in遍历键名,for...of遍历键值
var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}
  • for...of可以配合break、continue和return使用
for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}

Generator

概念

Generator 函数是 ES6 提供的一种异步编程解决方案。
Generator 函数是一个状态机,封装了多个内部状态。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,必须调用next()才会执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象(yield)。

yield 表达式

遍历器对象的next方法的运行逻辑如下。

  • (1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
  • (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
  • (3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
  • (4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
  • yield其實是兩個動作的合體:丟東西出去->等東西進來;

每次next()都會跑到yield丟東西出來的那個步驟

//输入输出:yield右边为输出(next返回值的value,最后一次由return返回),左边为输入(接受的是next的参数,第一次由函数参数传入),
function * input(){
    let array = [], i = 4;
    while(i) {
        array.push(yield array);
        i --;
    }
}

var gen = input();
console.log(gen.next("西")) // { value: [], done: false }
console.log(gen.next("部")) // { value: [ '部' ], done: false }
console.log(gen.next("世")) // { value: [ '部', '世' ], done: false }
console.log(gen.next("界")) // { value: [ '部', '世', '界' ], done: false }

next 方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

for...of 循环

for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

next()、throw()、return() 的共同点

next()、throw()、return()这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。

  • next()是将yield表达式替换成一个值。
const g = function* (x, y) {
  let result = yield x + y;
  return result;
};

const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}

gen.next(1); // Object {value: 1, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = 1;
  • throw()是将yield表达式替换成一个throw语句。
gen.throw(new Error('出错了')); // Uncaught Error: 出错了
// 相当于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));
  • return()是将yield表达式替换成一个return语句。
gen.return(2); // Object {value: 2, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = return 2;

yield* 表达式

在 Generator 函数内部,调用另一个 Generator 函数

function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  }
  yield 'y';
}

for (let v of bar()){
  console.log(v);
}
// "x"
// "a"
// "b"
// "y"

作为对象属性的 Generator 函数

let obj = {
  * myGeneratorMethod() {
    ···
  }
};
//等价于
let obj = {
  myGeneratorMethod: function* () {
    // ···
  }
};

应用

  • ajax
function* main() {
  var result = yield request("http://some.url");
  var resp = JSON.parse(result);
    console.log(resp.value);
}

function request(url) {
  makeAjaxCall(url, function(response){
    it.next(response);
  });
}

var it = main();
it.next();

更多Generator 函数的异步应用:http://es6.ruanyifeng.com/#do...

http://jsfiddle.net/Yoghurts/...
ES7引入 async await,使其更加好用

利用ES6中的Generator实现并发编程

参考资料
ES6实现
// 暂停
function sleep(numberMillis) {
    var now = new Date();
    var exitTime = now.getTime() + numberMillis;
    while (true) {
        now = new Date();
        if (now.getTime() > exitTime){
            return;
        }
    }
}

// 消费者
function* consumer(name) {
    console.log(`${name}准备吃包子啦!`);
    while (true) {
        var baozi = yield;
        baozi += 1;
        console.log(`第${baozi}个包子来了,被分成了两份,一份被${name}吃了!`);
    }
}

// 生产者
function producer(name) {
    c1 = consumer('A');
    c2 = consumer('B');
    console.log(`${name}:我开始准备做包子了!`);
    c1.next();
    c2.next();
    for (let i = 0; i < 10; i++) {
        sleep(1000);
        c1.next(i);
        c2.next(i);
    }
}

// 小明开始生产包子,A和B同时开始吃包子
producer('小明')
运行结果
小明:我开始准备做包子了!
A准备吃包子啦!
B准备吃包子啦!
第1个包子来了,被分成了两份,一份被A吃了!
第1个包子来了,被分成了两份,一份被B吃了!
第2个包子来了,被分成了两份,一份被A吃了!
第2个包子来了,被分成了两份,一份被B吃了!
第3个包子来了,被分成了两份,一份被A吃了!
第3个包子来了,被分成了两份,一份被B吃了!
第4个包子来了,被分成了两份,一份被A吃了!
第4个包子来了,被分成了两份,一份被B吃了!
第5个包子来了,被分成了两份,一份被A吃了!
第5个包子来了,被分成了两份,一份被B吃了!
第6个包子来了,被分成了两份,一份被A吃了!
第6个包子来了,被分成了两份,一份被B吃了!
第7个包子来了,被分成了两份,一份被A吃了!
第7个包子来了,被分成了两份,一份被B吃了!
第8个包子来了,被分成了两份,一份被A吃了!
第8个包子来了,被分成了两份,一份被B吃了!
第9个包子来了,被分成了两份,一份被A吃了!
第9个包子来了,被分成了两份,一份被B吃了!
第10个包子来了,被分成了两份,一份被A吃了!
第10个包子来了,被分成了两份,一份被B吃了!

Class

定义类

//以前
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

//es6
class Point {
//构造方法
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
var p = new Point(1, 2);

继承类

class Point {
}

class ColorPoint extends Point {
}

module

简介

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

严格模式

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

严格模式主要有以下限制:

  • 变量必须声明后再使用
  • 函数的参数不能有同名属性,否则报错
  • 不能使用with语句
  • 不能对只读属性赋值,否则报错
  • 不能使用前缀 0 表示八进制数,否则报错
  • 不能删除不可删除的属性,否则报错
  • 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
  • eval不会在它的外层作用域引入变量
  • eval和arguments不能被重新赋值
  • arguments不会自动反映函数参数的变化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局对象
  • 不能使用fn.caller和fn.arguments获取函数调用的堆栈
  • 增加了保留字(比如protected、static和interface)

export 命令

定义模块的对外接口。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。
以下是几种用法:

//------输出变量------
export var firstName = 'Michael';
export var lastName = 'Jackson';
//等价于
var firstName = 'Michael';
export {firstName}; //推荐,能清除知道输出了哪些变量
//------输出函数或类------
export function multiply(x, y) {
  return x * y;
};
//------输出并as重命名------
var v1 = 'Michael';
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2
};
//------输出default------
export default function () { ... }

注意:export default在一个模块中只能有一个。

import 命令

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
以下是几种用法,必须和上面的export对应:

//------加载变量、函数或类------
import {firstName, lastName} from './profile.js';
//------加载并as重命名------
import { lastName as surname } from './profile.js';
//------加载有default输出的模块------
import v1 from './profile.js';
//------执行所加载的模块------
import 'lodash';
//------加载模块所有输出------
import  * as surname from './profile.js';

复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。

export { foo, bar } from 'my_module';

// 等同于
import { foo, bar } from 'my_module';
export { foo, bar };

Symbol

原始数据类型Symbol,表示独一无二的值。

let s = Symbol();

typeof s
// "symbol"

Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

Proxy

代理器(拦截器),Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

var proxy = new Proxy(target, handler);
  • target参数表示所要拦截的目标对象
  • handler参数也是一个对象,用来定制拦截行为。
var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return target.key;
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return target.key=value;
  }
});

BWrong
363 声望304 粉丝

愿你走出半生,归来仍是少年