Skip to main content

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