前置知识
- 展开运算符 剩余运算符
- 观察者模式,发布订阅模式
- 柯里化
- Promises/A+规范
展开运算符 剩余运算符
展开运算符,剩余运算符的小结
展开运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值,等号右边,或者放在实参上
rest01(...arr)
// 字符串数组化
var str='loycoder';
var arr3= [...str];
剩余运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组,在等号左边,或者放在形参上。
function rest01(...arr) {}
var [a,...temp]=[1, 2, 4];
观察者模式,发布订阅模式
当问到大多数面试者两种模式大都可以侃侃而谈。 这里推荐一篇关于两者异同的好文。 Observer vs Pub-Sub pattern
面试回答亮点
-
观察者模式大多数是以同步方式实现的,当某个事件发生时,被观察者调用其所有观察者的适当方法。发发布订阅则大都使用于异步方式(使用消息队列)。
-
发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的 —— 经纪人Broker
-
观察者模式,多用于单个应用内部
-
发布订阅模式,则更多的是一种跨应用的模式(cross-application pattern),比如我们常用的消息中间件
柯里化和反柯里化
柯里化和反柯里化是高阶函数的一种应用,随着对源码的深入不再停留在苍白的概念层面。 函数可以作为参数传递到函数中,这个作为参数的函数叫回调函数,而拥有这个参数的函数就是高阶函数,回调函数在高阶函数中调用并传递相应的参数,这种函数式编程的方式,在高阶函数执行时,由于回调函数的内部逻辑不同,高阶函数的执行结果也不同,非常灵活。
// 通用的柯里化
const add = (a, b, c, d, e) => {
return a + b + c + d + e;
};
const curring = (fn,arr = [])=>{
let len = fn.length
return (...args)=>{
arr = arr.concat(args); // [1] [1,2,3] < 5
if(arr.length < len){
return curring(fn,arr)
}
return fn(...arr)
}
}
let r = curring(add)(1)(2)(3)(4)(5); // [1,2,3,4,5]
console.log(r);
这事说来话长,但也是进阶必备技能。打算在中间件一文中展开讨论。
入门可以看大神的文章 JS中的柯里化(currying)
Promises/A+规范
Promises/A+规范的代码实现
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
const resolvePromise = (promise2, x, resolve, reject) => {
//因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2
// 不能自己等待自己完成
if(promise2 === x){
return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`));
}
// 这里判断是否是promise的方法是判断是否具有then方法
if((typeof x === 'object' && x!==null) || typeof x ==='function'){
//called变量主要是用来判断如果resolvePromise函数已经resolve或者reject了,那就不需要在执行下面的resolce或者reject。
let called;
try{
let then = x.then; // 取then的时候 可能会发生异常 getter
if(typeof then === 'function'){ // 如果是函数
//用call的原因是因为then方法里面this指向的问题。
then.call(x,y=>{ // 就让此函数调用即可
if(called) return
called = true // y有可能还是个promise,递归到最后直到不是Promise对象。
resolvePromise(promise2,y,resolve,reject)
},r=>{
if(called) return;
called = true
//一次错误就直接返回
reject(r)
})
}else{
//如果是个普通对象就直接返回resolve作为结果
resolve(x);
}
}catch(e){
if(called) return;
called = true
reject(e);
}
}else{
// 不是promise
resolve(x); // 直接将结果抛出即可
}
};
class Promise {
constructor(executor) { // executor执行器,包含两个参数,分别是resolve和reject,new Promise这个executor就会执行
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
this.status = PENDING;
this.onResolvedCallbacks = [];//定义存放成功的回调的数组
this.onRejectedCallbacks = [];//定义存放失败回调的数组
//此处应用了发布订阅模式,状态确认时把订阅的都执行一下
let resolve = value => { // 成功
if (this.status === PENDING) { // 只有当状态是pending的时候
this.value = value; // 将值保存起来
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
let reject = reason => { // 失败
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject); // 出错了 直接退出即可
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) { // 两个回调可能是可选参数
//解决onFufilled,onRejected没有传值的问题
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
//因为错误的值要让后面访问到,所以这里也要抛出个错误,不然会在之后then的resolve中捕获
onRejected = typeof onRejected === 'function'?onRejected:err=>{throw err};
//因为在.then之后又是一个promise对象,所以这里肯定要返回一个promise对象。为了链式调用
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
//难点在于onFulfilled的返回值有可能还是一个异步的Promise,需要等待其返回状态
//如果不是,直接作为promise2的resolve结果
let x = onFulfilled(this.value);
//抽离出一个公共方法来判断他们是否为promise对象
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
}
Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}
module.exports = Promise;
Promises 的其他方法
-
catch
语法糖 then(null,errCallback)
-
finally
在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数
-
all
all 方法接受一个 promise 对象的数组,等数组中所有的 promise 对象的状态变为 fulfilled,然后返回结果,其结果也是一个数组,数组的每个值对应的是 promise 对象的内部结果。
-
race
race 方法接受一个 promise 对象的数组,但是它只需要有一个 promise 变为 fulfilled 状态就会返回结果。
一些面试题
var promise = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 3000)
})
promise.then(2).then((n) => {
console.log(n)
});
结果是1 ,因为then(2)非函数,会穿透到下一个then
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
其实就是抖了个机灵,打印then: Error: error!!! ,而非throw 所以不走catch分支
const promise = Promise.resolve()
.then(() => {
return promise
})
promise.catch(console.error)
.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。参加源码
var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(value);
return value*2;
}).then(function(value){ //第二个then
console.log(value);
}).then(function(value){ //第三个then
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ //第四个then
console.log(value);
return Promise.reject('reject');
}).then(function(value){ //第五个then
console.log('resolve: '+ value);
}, function(err){
console.log('reject: ' + err);
})
运行结果
1
2
undefined
"resolve"
"reject: reject"
这一题是对基础知识的一个总结
Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。 then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。
两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象。 这两个参数的返回值可以是以下三种情况中的一种:
- return 一个同步的值 ,或者 undefined(当没有返回一个有效值时,默认返回undefined),then方法将返回一个resolved状态的Promise对象,Promise对象的值就是这个返回值。
- return 另一个 Promise,then方法将根据这个Promise的状态和值创建一个新的Promise对象返回。
- throw 一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。 根据以上分析,代码中第一个then会返回一个值为2(1*2),状态为resolved的Promise对象,于是第二个then输出的值是2。 第二个then中没有返回值,因此将返回默认的undefined,于是在第三个then中输出undefined。 第三个then和第四个then中分别返回一个状态是resolved的Promise和一个状态是rejected的Promise,依次由第四个then中成功的回调函数和第五个then中失败的回调函数处理。