简介
- 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;
}
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。