一、面向对象编程
1、实现new
关键字
实现思路:
- 要点1:创建新对象,作为要返回的对象
- 要点2:把新对象的原型指针
__proto__
指向构造函数的原型 - 要点3:执行构造函数,并把this指针指向当前对象,
- 要点4:构造函数的结果是非空对象的话就返回这个结果,否则返回新创建的对象
function myNew(contor, ...args) {
let obj = Object.create(contor.prototype);
const rst = contor.call(obj, ...args);
return typeof rst === "object" ? rst || obj : obj;
}
用例 在线链接
2、实现寄生组合继承
寄生组合继承的思想是借用构造函数继承父类属性,然后通过原型链的混成形式来继承方法。
实现思路:
- 要点1:子类的构造函数执行父类的构造函数
- 要点2:子类的原型赋值为父类的原型。 在线链接
function Parent(name) {
this.name = name
}
Parent.prototype.sayHello = (name) => {
console.log('hello', name)
}
// 要点1:子类的构造函数执行父类的构造函数
// 要点2:子类的原型赋值为父类的原型。
function Child(name,subName) {
Parent.call(this,name)
// 这里要调用父类的方法之后再调用。
this.subName = subName
}
Child.prototype = Object.create(Parent.prototype, {
constructor: Child
})
Child.prototype.saySubName= function saySubName () {
// 这里用箭头函数就获取不到this
console.log('mySubName is', this.subName)
}
// 测试用例
const child = new Child('Jack','wang')
console.log(child.name)
console.log(child.subName)
console.log(child.subName)
console.log(child.sayHello('lili'))
console.log(child.saySubName())
3、实现instanceof
`instanceof `是判断一个对象是否是另一个对象的实例的方法。
实例对象有个原型指针__proto__,执行该实例的构造函数的原型prototype
也就是instance.__proto__ = constro.prototyoe
obj.__proto__
,可以用Object.getPrototypeOf
这个兼容写法
要点在于:
- 通过原型链向上搜索,直到原型链的终点,看__proto__ 是否相等。 在线链接
function myInstanceof(left, right) {
const prototype = right.prototype;
while (left !== null) {
const proto = Object.getPrototypeOf(left);
if (proto === prototype) {
console.log('====',proto,prototype)
return true;
}
left = proto;
}
return false;
}
测试用例:
class Father {
constructor(tall) {
this.height = tall
}
}
const jack = new Father("180");
console.log('用例1:',myInstanceof(jack, Father)); // true
console.log('用例2:',myInstanceof([], Array)); // false
console.log('用例3',myInstanceof(123, Father)); // false
console.log('用例4',myInstanceof(123,[] )); // false
4、实现Object.create
`Object.create`是使用现有对象来提供新对象的原型指针`__proto__`,来创建一个新对象。
并且可以指定新对象的属性值。这个过程类似于寄生式继承的步骤:
1、新建一个对象,将新对象的原型设置成指定的原型
2、寄生式继承来为对象添加属性。
function create (proto){
function F() {}
F.prototype = proto;
return new F()
}
//寄生式继承
function myCreate(proto, propertiesObject) {
const clone = create()
// 增强对象
if (propertiesObject && typeof propertiesObject === "object") {
for (let key in propertiesObject) {
clone[key] = propertiesObject[key];
}
}
return clone;
}
二、异步编程
1、实现简易的promise
Promise是个构造函数,接收一个处理器函数executor作为参数。
-
要点1:有三种状态
pending
、fulfilled
、rejected
-
要点2:
executor
立即执行。 -
要点3:接收
resolve
和reject
两个函数作为参数。所以需要定义两个函数。 异步任务成功返回结果时,调用resolve
,异步任务失败且返回失败结果或者executor
执行出错时执行reject
-
要点4:当状态为 pending 时,
onFulfilled
函数由一个队列维护。当状态为 fulfilled 时,onFulfilled
函数直接执行,rejected
同理。所以需要有个数组来存回调函数,和一个存结果的状态。总的来说,就是定义一个类和一个成功回调和失败回调。 接受一个立即函数,成功了执行成功回调,失败了执行失败回调。
export default class MyPromise {
constructor(executor) {
if (typeof executor !== "function") {
throw new Error('Promise resolver 1 is not a function')
}
if (this instanceof MyPromise) {
throw new Error(`${this} is not a promise`)
}
// 要点1
this.status = "pending";
this.result = "";
this.errorRst = "";
// 要点4
this.onFullfilledCallback = [];
this.onRejectedCallback = [];
let self = this;
// 要点3:定义成功回调
function onFulfilled(value) {
if (self.status === "pending") {
self.status = "fullfilled";
self.result = value;
if (self.onRejectedCallback.length) {
self.onFullfilledCallback.forEach((cb) => {
typeof cb === "function" && cb(value);
});
}
}
}
// 要点3:定义失败回调
function onRejected(error) {
if (self.status === "pending") {
self.status = "rejected";
self.result = error;
if (self.onRejectedCallback.length) {
self.onRejectedCallback.forEach((cb) => {
typeof cb === "function" && cb(error);
});
}
}
}
// 要点2、要点3
try {
executor(onFulfilled, onReject);
} catch (e) {
onReject(e);
}
}
2、实现Promise.prototype.then
then()方法是Promise原型链的方法,返回一个Promise。它最多接受2个参数,成功回调和失败回调。
实现思路如下:
- 要点1:then返回的是个 Promise
- 要点2:如果当前状态是pending状态,需要把回调存进pending队列里面
- 要点3:then里面的方法是异步执行的,这里是用setTimeout来模拟
- 要点4:回调函数onResovled返回的是promise1,那么这个结果promis1e的执行resovle时候,整个then Promise才是resovled状态。
- 要点5:回调函数onResovled是一个常量的话,发生值穿透。把onResoved变成一个返回value的函数就好了。 链接🔗
MyPromise.prototype.then = function (onResovled, onRejected) {
const self = this;
//要点5:值穿透
onResovled = typeof onResovled === "function" ? onResovled : (value) => value;
onResovled = typeof onResovled === "function" ? onResovled : (value) => value;
// 要点1:返回一个Promise1
return new MyPromise((resolve, reject) => {
function _handle(callback) {
try {
const rst = callback(self.result);
// 要点4:回调返回一个Promise
if (rst instanceof MyPromise) {
rst.then((value) => {
resolve(value);
})
.catch((err) => {
reject(err);
});
} else {
resolve(rst);
}
} catch (e) {
reject(e);
}
}
switch (self.status) {
// 要点2:pending时存进队列
case "pending":
self.onFullfilledCallback.push(() => {
_handle(onResovled);
});
self.onRejectedCallback.push(() => {
_handle(onRejected);
});
break;
case "fullfilled":
// 要点3:异步执行
setTimeout(() => {
_handle(onResovled);
}, 0);
break;
case "rejected":
setTimeout(() => {
_handle(onRejected);
});
break;
default:
break;
}
});
3、实现Promise.prototype.resolve
Promise.resolve返回一个新的Promise。如果参数本身就是一个Promise对象,则直接返回这个Promise对象。
MyPromise.prototype.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(value) => {
resolve(value);
},
(err) => {
reject(err);
}
);
} else {
resolve(value);
}
});
};
4、实现Promise.prototype.finally
Promise.finally无论Promise成功或失败都会执行。无法知道promis的终态,不接受任何参数。
- 要点1:返回一个Promise
- 要点2:返回原来的值,把值穿透到下一个。 链接🔗
MyPromise.prototype.finally = function (callback) {
let P = this.constructor();
return this.then(
// 如果callback 里面是一个Promise,需要等待它的结果
(value) => P.resolve(callback()).then(() => value),
(err) => P.resolve(callback()).then(() => {
throw err;
})
);
};
5、实现Promise.prototype.all
Promise.all接收一个promise的iterable类型,返回一个Promise实例。
- resovle回调的结果是一个数组
- reject 的是第一个抛出的错误信息 链接🔗
MyPromise.prototype.all = function (promiseArr) {
return new Promise((resolve, reject) => {
let promiseRst = [];
let index = 0;
promiseArr.forEach((p, i) => {
// 如果p一个Promise,需要他的结果
Promise.resolve(p)
.then((rst) => {
promiseRst[i] = rst;
if (++index === promiseArr?.length) {
resolve(promiseRst);
}
})
.catch((err) => reject(err));
});
});
};
6、实现Promise.prototype.allSettled
Promise.allSettled返回一个Promise,Promise的结果是带有对象的数组,每个对象表示对于的Promise结果。
区别于Promise.all的异常会短路且不关心每个promise状态的结果,
MyPromise.allSettled = function (promiseArr) {
let index = 0;
const rst = [];
let len = promiseArr.length;
if (!len) {
return Promise.resolve(rst);
}
return new Promise((resolve) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p)
.then((value) => {
rst[i] = { status: "fulfilled", value };
if (++index === len) {
resolve(rst);
}
})
.catch((err) => {
rst[i] = { status: "rejected", value: err };
if (++index === len) {
resolve(rst);
}
});
});
});
};
7、实现Promise.prototype.race
Promise.race接收一个promise的iterable类型,返回一个新的promise,
一旦迭代器中某个promise解决和拒绝,这个promise就会返回第一个结果。
MyPromise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach((promise) => {
// 为了把arr的所有值都转成promise
Promise.resolve(promise)
.then((value) => {
resolve(value);
})
.catch((err) => {
reject(err);
});
});
});
};
8、实现Promise.prototype.any
Promise.any区别于Promise.race,返回的是第一个成功的Promise。
都失败时,才reject
MyPromise.prototype.any = function (promiseArr) {
let index = 0;
const len = promiseArr.length;
if (!len) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
promiseArr.forEach((p) => {
Promise.resolve(p)
.then((rst) => {
resolve(rst);
})
.catch(() => {
// 判断是否都失败了
if (++index === len) {
// new AggregateError
reject(new Error("All promises were rejected"));
}
});
});
});
};
9、实现Promisify
把同步的回调函数改成promise
首先了解,nodeCallback的规范:
- 回调函数在主函数参数的位置是最后一个
- 回调函数的第一个参数是error 例如:
function main(err, b, c, callback) {
let data = b+c
callback(err,data)
}
所以,实现思路是把结果由原先的放在callback中返回,改成放在Promise中返回。 最终用法如下:
var func1 = function (a, b, c, callback) {
let rst = a + b + c;
callback(null, rst);
};
var func2 = promisify(func1);
func2(1, 2, 3).then((rst) => {
console.log("rst", rst);
});
可以知道,Promisify返回的是一个高阶函数,这个函数返回一个promise。 我们可以执行传入的函数,改写回调函数callback,在原本的callback里面resolve/reject promise的结果。
所以,我们可以如下实现:
const promisify = (fnc) => (...args) => {
return new Promise((resolve, reject) => {
fnc.call(this, ...args, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
其他promise面试题拓展:
10、实现可中断的promise
单个promise一开始,就无法中断。但是多个promise.all和promise.race的有异常短路的特点,
可以中断promise。
所以可以将目标promise和中断的promise放在一起,利用promise.race竞速的特点,
中断这组promise,达到中断单个promise的目的。
实现思路:
- 把abort方法赋值为成拒绝结果的promise的reject。一旦被调用,race的promise直接返回。 链接🔗
function abortWrapper1(promise) {
let abort;
// 注意这里,abort方法没有被调用时,reject不会被调用。
// 也就是说,这个p2的promise没有返回。
let p2 = new Promise((_, reject) => (abort = reject));
let p = Promise.race([promise, p2]);
p.abort = abort;
return p;
}
使用例子:
const request = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("收到服务端数据");
}, 3000);
});
const req = abortWrapper1(request);
req.then((res) => console.log(res)).catch((e) => console.log(e));
setTimeout(() => req.abort("用户手动终止请求1"), 2000); // 这里可以是用户主动点击