Handwriting Question
🤖WARNING
The English translation was done by AI.
Promise
Code Details
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
status = PENDING;
value = null;
reason = null;
// Used to store success and failure callback functions
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) => {
// Handling cases when values are not passed
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) {
// Handling cases when the executor is asynchronous
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
Related knowledge points: prototype chain, proto, destructuring assignment, arguments
- Create a new object
- Get the constructor from arguments and get the prototype through the constructor
- Link the newly created object to the prototype through
__proto__
- Bind this and execute the constructor
function create() {
// Create an empty object
let obj = {};
// Get the constructor, delete the first element and return it
let Con = [].shift.call(arguments);
// Link to the prototype
obj.__proto__ = Con.prototype;
// Bind this and execute the constructor
let result = Con.apply(obj, arguments);
// Ensure that the new object is an object
return typeof result === 'object' ? result : obj;
}
instance of
Related knowledge point: prototype chain lookup
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;
}
// Look up the prototype chain
objPrototype = objPrototype.__proto__;
}
}
console.log(myInstanceOf({}, Object)); // true
call and apply
Related knowledge:
- The first parameter is the window when not passed, in non-strict mode
- Approach: add the function to the new object and execute the function in the context of the new object, then delete it
Function.prototype.myCall = function (...args) {
const context = args[0] || window;
// this refers to the function being called
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 refers to the function being called
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
Related knowledge:
- Function currying
Return a function that can be called or used as a constructor 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);
// Handling cases when new is used
if (this instanceof F) {
return new binder(...restArgs, ...arguments);
}
// Function currying
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);
Currying
function curry(fn) {
return function curr(...args) {
// fn.length is the number of formal parameters of 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
Asynchronous Version
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
Type Checking
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]';
}