跳到主要内容

手写题

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]';
}