手写题
Promise
详细代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
status = PENDING;
value = null;
reason = null;
// 用来存储成功、失败回调函数
onFulfilledCallbacks = [];
onRejectedCallbacks = [];
constructor(executor) {
try {
executor(this._resolve, this._reject);
} catch (error) {
this._reject(error);
}
}
static resolve = p => {
if (p instanceof MyPromise) {
return p;
}
return new MyPromise(resolve => {
resolve(p);
});
};
static reject = reason => {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
static all = promises => {
return new MyPromise((resolve, reject) => {
let result = [];
let resolvedCount = 0;
promises.forEach((promise, index) => {
promise.then(
res => {
result[index] = res;
resolvedCount++;
resolvedCount === promises.length && resolve(result);
},
error => reject(error)
);
});
});
};
static allSettled = promises => {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
const convertedPromises = promises.map(p =>
Promise.resolve(p).then(resolveHandler, rejectHandler)
);
return Promise.all(convertedPromises);
};
static race = promises => {
return new MyPromise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(
res => {
resolve(res);
},
error => reject(error)
);
});
});
};
_resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
while (this.onFulfilledCallbacks.length) {
this.onFulfilledCallbacks.shift()(value);
}
}
};
_reject = reason => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason);
}
}
};
then = (onFulfilled, onRejected) => {
// 处理不传值情况
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: reason => {
throw reason;
};
const thenPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
try {
const fulfilledObj = onFulfilled(this.value);
resolvePromise(fulfilledObj, resolve, reject);
} catch (error) {
reject(error);
}
} else if (this.status === REJECTED) {
onRejected(this.reason);
} else if (this.status === PENDING) {
// 处理executor为异步的情况
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
});
return thenPromise;
};
}
function resolvePromise(p, resolve, reject) {
if (p instanceof MyPromise) {
p.then(resolve, reject);
} else {
resolve(p);
}
}
new
涉及知识点:原型链、proto、解构赋值、arguments
- 创建一个对象
- 从 arguments 中获取构造函数,并通过构造函数获取原型
- 将新建的对象通过
__proto__
链接到原型上 - 绑定 this 并执行构造函数
function create() {
// 创建一个空的对象
let obj = {};
// 获得构造函数,shift删除第一个元素并返回该元素
let Con = [].shift.call(arguments);
// 链接到原型
obj.__proto__ = Con.prototype;
// 绑定 this,执行构造函数
let result = Con.apply(obj, arguments);
// 确保 new 出来的是个对象
return typeof result === 'object' ? result : obj;
}
instance of
涉及知识点:原型链查找
function myInstanceOf(obj, cons) {
if (!obj || typeof obj !== 'object') {
return false;
}
let objPrototype = obj.__proto__;
let consPrototype = cons.prototype;
while (true) {
if (objPrototype === null) {
return false;
}
if (objPrototype === consPrototype) {
return true;
}
// 向上查找原型链
objPrototype = objPrototype.__proto__;
}
}
console.log(myInstanceOf({}, Object)); // true
call 和 apply
涉及知识点:
- 第一个参数不传时,非严格模式下为 window
- 思路:给新对象添加该函数,并在新对象的上下文中执行该函数,执行完后删除即可
Function.prototype.myCall = function (...args) {
const context = args[0] || window;
// this为调用它的函数
console.log(this);
context.excuteFunc = this;
const result = context.excuteFunc(...args.slice(1));
delete context.excuteFunc;
return result;
};
Function.prototype.myApply = function (...args) {
const context = args[0] || window;
// this为调用它的函数
console.log(this);
context.excuteFunc = this;
let result;
if (args[1]) {
result = context.excuteFunc(...args[1]);
} else {
result = context.excuteFunc();
}
delete context.excuteFunc;
return result;
};
const foo = {
name: 'alan'
};
function sayName(...args) {
console.log(this.name, args);
}
sayName.myCall(foo, 'args1', 'args2');
sayName.myApply(foo, ['args1', 'args2']);
bind
涉及知识:
- 函数柯里化
返回一个函数,可调用,可当作构造函数 new
Function.prototype.myBind = function (thisArg, ...restArgs) {
if (typeof this !== 'function') {
throw new TypeError('not a function');
}
const binder = this;
return function F() {
console.log(this);
// 处理new的情况
if (this instanceof F) {
return new binder(...restArgs, ...arguments);
}
// 函数柯里化处理
return binder.apply(thisArg, restArgs.concat(...arguments));
};
};
const a = {
outer: 'outer'
};
function say(name, age) {
this.name = name;
this.age = age;
console.log(this.outer, name, age);
}
const curry = say.myBind(a, 'alan');
const c = new curry(23);
console.log(c);
// outer alan 21
curry(21);
柯里化
function curry(fn) {
return function curr(...args) {
// fn.length是fn的形参个数
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...args2) => curr.apply(this, args.concat(args2));
};
}
function add(a, b, c) {
return a + b + c;
}
const curried = curry(add);
console.log(curried(1, 2, 3), curried(1)(3)(3), curried(1, 1)(1));
compose
function compose(...fns) {
return function (args) {
return fns.reduceRight((result, fn) => fn(result), args);
};
}
function addTwo(x) {
return x + 2;
}
function double(x) {
return x * 2;
}
function addThree(x) {
return x + 3;
}
console.log(compose(addTwo, double, addThree, double)(1)); // 12
异步版本
function compose(...fns) {
return function (args) {
return fns.reduceRight(
(promise, fn) => promise.then(fn),
Promise.resolve(args)
);
};
}
function sleep(wait) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, wait);
});
}
async function addTwo(x) {
await sleep(100);
return x + 2;
}
async function double(x) {
await sleep(100);
return x * 2;
}
function addThree(x) {
return x + 3;
}
compose(
addTwo,
double,
addThree,
double
)(1).then(res => {
console.log(res);
}); // 12
类型判断
function isObject(x) {
return Object.prototype.toString.call(x) === '[object Object]';
}
function isArray(x) {
return Object.prototype.toString.call(x) === '[object Array]';
}
function isDate(x) {
return Object.prototype.toString.call(x) === '[object Date]';
}