1. 1. Promise
    1. 1.1. 基本用法
    2. 1.2. 用 promise 处理异步场景
  2. 2. Symbol
  3. 3. Proxy
  4. 4. 统一码
  5. 5. 进制数支持
  6. 6. Reflect
  7. 7. tail calls 尾调用
ES6 核心向新特性(03)

Promise

Promise 是异步编程的一种解决方案,用来避免异步操作函数里的多层嵌套回调(callback)问题。Promise 代表一个异步操作的执行返回状态,这个执行回状态在 Promise 对象创建时是未知的,它允许为异步操作的成功或失败指定处理方法。

基本用法

通常 Promise 的状态有三种:Fulfilled 状态表示执行成功;Rejected 状态表示执行失败;Pending 状态表示正在执行中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let status = 1;
let promise = new Promise(function(resolve, reject) {
if(status === 1) {
resolve('Fulfilled');
} else {
reject('Rejected');
}
});
Promise.then(function(msg) {
console.log('success1:' + msg);
return msg;
}, function(msg) {
console.log('fail1:' + msg);
return msg;
}).then(function(msg) {
console.log('success2:' + msg);
}, function(msg) {
console.log('fail2:' + msg);
});
// success1: Fulfilled
// success2: Fulfilled

promise 的 then 方法接受两个处理函数,当 status 为 1 时执行 Fulfilled 成功调用,否则 Rejected 失败调用。返回的状态给第二个 then 方法处理。then 方法可以将传入参数的返回值一直传递下去,如果是异步的场景,就可以用这种方式来解决多层回调嵌套的问题了。

用 promise 处理异步场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 希望它依次异步输出 A B C D
let promise = new Promise(function(resolve) {
setTimeout(function() {
console.log('A');
resolve();
}, 3000); // 延迟3秒打印A
});
// 使用 then 来链式处理流程
promise.then(function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('B');
resolve();
}, 2000); // 延迟2秒打印B
});
}).then(function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('C');
resolve();
}, 1000); // 延迟1秒打印C
});
}).then(function() {
return new Promise(function(resolve, reject) {
console.log('D'); // 不延迟打印D
});
});

通过在不同的 then 处理方法中返回一个新的 promise 来解决。返回新的 promise 里面具有resolve()reject()方法,只有当它的 resolve 或 reject 被调用时,promise 方法才会继续执行,进入下一个 then 方法中操作。设置在异步函数完成的最后调用resolve()就可以有效控制 promise 进入下一个 then 方法执行。

Symbol

Symbol 是除布尔值、数值等六种数据类型外的第七种数据类型。属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

1
2
3
4
5
6
7
8
9
10
11
12
13
let object = {};
let name = Symbol();
let family = Symbol();

object[name] = 'ouven';
object[family] = 'zhang';

console.log(object);
// {
// Symbol(): 'ouven',
// Symbol(): 'zhang',
// }
console.log(typeof name); // symbol

Proxy

Proxy 可以用来拦截某个对象的属性访问方法,然后重载对象的.运算符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let object = new Proxy({}, {
get: function(target, key, receiver) {
console.log(`getting ${key}`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(`setting ${key}`);
return Reflect.set(target, key, value, receiver);
}
});

// 赋值或定义值都会输出
// getting value
// setting value
object['value'] = 3;

上面代码对一个空对象架设了一层拦截,重定义了属性的读取(get)和设置(set)行为。
基本用法

1
var proxy = new Proxy(target, handler);

target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

  • get(target, propKey, receiver): 拦截对象属性的读取,如proxy.fooproxy['foo']
  • set(target, proKey, value, receiver): 拦截对象属性的设置,如proxy.foo = vproxy['foo'] = v
  • ……

统一码

es6 字符串支持新的 Unicode 文本形式,同时也增加了新的正则表达式修饰符u来处理统一码。尽管如此,在实际的开发中,这样处理仍会降低程序可读性和处理速度,所以目前不建议使用。

进制数支持

es6 增加了对二进制(b)和八进制(o)数字面量的支持。

1
2
0b111110111 === 503 // true
0o767 === 503 // true

Reflect

Reflect 可以理解为原有对象上的一个引用代理,它用于对原有对象进行赋值或取值操作,但不会触发对象属性的 getter 或 setter 调用,而直接使用 = 对对象进行赋值或取值操作会自动触发 getter 或 setter 方法。

tail calls 尾调用

tail calls 尾调用保证了函数尾部调用时调用栈有一定的长度限制,这使得递归函数即使在没有限制输入时也能保证安全性而避免发生错误。

1
2
3
4
5
6
7
8
function factorial(n, start = 1) {
if(n < = 1) {
return start;
}
return factorial(n - 1, n * start);
}
// 默认情况下会发生栈溢出,但是在 es6 中是可以安全执行的
factorial(100000);

基本概念
尾调用是指某个函数的最后一步是调用另一个函数。递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生”栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生”栈溢出”错误。
注意点
尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。

学习参考:
ECMAScript 6入门——阮一峰
尾调用优化
《现代前端技术解析》