1、手写Promise
class Promise2 {state = "pending";callbacks = [];constructor(fn) {fn(this.resolve.bind(this), this.reject.bind(this));}resolve(result) {if (this.state !== "pending") return;this.state = "fullFilled";nextTick(() => {this.callbacks.forEach((handle) => {if (typeof handle[0] === "function") {handle[0].call(undefined, result);}});});}reject(reason) {if (this.state !== "pending") return;this.state = "rejected";nextTick(() => {this.callbacks.forEach((handle) => {if (typeof handle[1] === "function") {handle[1].call(undefined, reason);}});});}then(success, fail) {const handle = [];if (typeof success === "function") {handle[0] = success;}if (typeof fail === "function") {handle[1] = fail;}this.callbacks.push(handle);return this;}
}function nextTick(fn) {if (process !== undefined && typeof process.nextTick === "function") {return process.nextTick(fn);} else {var counter = 1;const observer = new MutationObserver(fn);var textNode = document.createTextNode(String(counter));observer.observe(textNode, {// 踪字符更改characterData: true,});counter += 1;textNode.data = String(counter);}
}// 方法返回一个Promise实例,此实例在 iterable 参数内所有的promise 都完成(resolved)时回调完成(resolve);
// 如果参数中 promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败promise的结果。Promise2.all = function(arrP) {let list = [];len = 0;return new Promise2((resolve, reject) => {for (let i = 0; i < arrP.length; i++) {arrP[i].then((val) => {list[i] = val;len++;len === arrP.length && resolve(list);},(err) => {reject(error);});}});
};Promise.prototype.myAll = (iterator) => {return new Promise((resolve, reject) => {const ret = []let count = 0Array.from(iterator).forEach((item, index) => {Promise.resolve(item).then(data => {ret[index] = datacount++if(count === iterator.length) {resolve(ret)}}, reject)})})
}// 方法返回一个Promise实例,一旦迭代器中的某个 promise 完成(resolved)或失败(rejected),返回的 promise 就会 resolve 或 rejectPromise2.race = function(arrP) {let flag1 = false;let flag2 = false;return new Promise2((resolve, reject) => {for (let i = 0; i < arrP.length; i++) {arrP[i].then((data) => {!flag2 && !flag1 && resolve(data);flag1 = true;return;},(error) => {!flag2 && !flag1 && reject(error);flag2 = true;return;});}});
};new Promise2((resolve, reject) => {let [val, time] = [Math.random(), Math.random() * 1000];setTimeout(() => {val > 0.2 ? resolve(val) : reject(val);}, time);
}).then((val) => console.log("promise 测试:", val),(err) => console.error("promise 测试:" + err)
);const getPList = () => {let arrP = [];for (let i = 0; i < 10; i++) {arrP[i] = new Promise2((resolve, reject) => {let [v, t] = [Math.random(), Math.random() * 1000];setTimeout(() => {v > 0.1 ? resolve(v) : reject(v);}, t);});}return arrP;
};Promise2.all(getPList()).then((data) => console.log("promise.all 测试:", data),(err) => console.error("promise.all 测试:" + err)
);Promise2.race(getPList()).then((data) => console.log("promise.race 测试:", data),(err) => console.error("promise.race 测试:" + err)
);
2、手写new
// 新生成一个对象
// 将构造函数的作用域赋值给新对象(即绑定新对象的 this)
// 执行构造函数中的代码(即为这个新对象添加属性)
// 返回新对象function myNew() {// 创建对象let obj = new Object();// 取第一个参数let fn = Array.prototype.shift.call(arguments);//obj.__proto__指向fn.prototypeobj.__proto__ = fn.prototype;// 执行结果let result = fn.apply(obj, arguments);return typeof result === "object" ? result : obj;
}function Person(name) {this.name = name;
}// var p1 = myNew(Person, "xx");
// console.log(p1.name);function P(name) {this.name = name;return 1;
}var p2 = myNew(P, "xm");
console.log(p2);
3、手写instanceof
实现思路:
1、leftVaule代表实例对象
2.rightVaule代表构造函数
3.利用typeof方法,判断输入的leftVaule是否为对象,如果不是,则返回false
4.遍历leftVaule的原型链,直到找到rightVaule的prototype,如果查找失败的话,返回false,反之,返回true
function myInstanceof(leftValue, rightValue) {if (typeof leftValue !== "object" || leftValue === null) return false;let leftProto = leftValue.__proto__;let rightProto = rightValue.prototype;while (true) {if (leftProto === null) {return false;}if (leftProto === rightProto) {return true;}leftProto = leftProto.__proto__;}
}myInstanceof([], Array);
4、并发请求限制
限制请求数,一个请求完成替换下一个请求
第一次分段
第二次添加下一个
控制startIndex与endIndex
终止态:返回值已等于请求数,执行cb
// class LimitFetch {}//
// 第一次分段
// 第二次添加下一个
// 控制startIndex与endIndex
// 终止态:返回值已等于请求数,执行cbclass LimitFetch {constructor(opts) {this.requestList = opts.requestList;this.limit = opts.limit;this.cb = opts.cb;this.startIndex = 0;this.result = {};this.resultCount = 0;this.batchRequest();}batchRequest(num) {const endIndex = this.startIndex + (num || this.limit);const len = this.requestList.length;for (let i = this.startIndex; i < endIndex; i++) {this.startIndex++;if (!this.requestList[i]) return;this.requestList[i]().then((res) => {this.result[i] = res;this.resultCount++;if (this.resultCount === len) {this.cb(this.result);}if (i < len - 1) {this.batchRequest(1);}});}}
}// 函数写法
function limitFetch(requestList, limit, cb) {let startIndex = 0;let results = {};let resultCount = 0;function batchRequest(num) {const endIndex = startIndex + (num || limit);for (let i = startIndex, len = requestList.length; i < endIndex; i++) {if (!requestList[i]) continue;startIndex++;requestList[i]().then((res) => {resultCount++;results[i] = res;if (i < len - 1) {batchRequest(1);}if (resultCount === len) {cb(results);}});}}batchRequest();
}let requestList = [];function fn(time) {return function () {// console.log(time);return new Promise((resolve, reject) => {setTimeout(() => {console.log(time);resolve(time);}, time);});};
}for (let i = 0; i < 5; i++) {requestList.push(fn(1 * 1000));
}
// limitFetch(requestList, 3, (res) => {
// console.log(res);
// });new LimitFetch({requestList,limit: 3,cb: (res) => {console.log(res);},
});// 限制并发
// 可以新增加
class BtRequest{constructor(opts) {this.limit = opts.limitthis.isRequest = falsethis.queue = []}add(fn) {this.queue.push(fn)if(!this.isRequest) {this.request()}}request(end) {this.isRequest = trueend = end || this.limitconst requestList = this.queue.splice(0, end)if(!requestList.length) {this.isRequest = false}requestList.forEach(item => {Promise.resolve(item()).then((res) => {console.log(1,res);this.request(1)})})}
}const request = new BtRequest({limit: 1})request.add(() => {console.log(100);// return 500
})
request.add(() => {console.log(200);// return 600})
request.add(() => {console.log(300);// return 700})
request.add(() => {console.log(400);// return 800})
5、手写发布订阅者
使用一个对象作为缓存
on 负责把方法发布到缓存的 EventName 对应的数组
emit 负责遍历触发(订阅) EventName 下的方法数组
off 找方法的索引,并删除
// 使用一个对象作为缓存
// on 负责把方法发布到缓存的 EventName 对应的数组
// emit 负责遍历触发(订阅) EventName 下的方法数组
// off 找方法的索引,并删除function indexOf(a, b) {return a.indexOf(b);
}class EventBus {constructor() {this.cache = {};}on(eventName, fn) {this.cache[eventName] = this.cache[eventName] || [];this.cache[eventName].push(fn);}off(eventName, fn) {const index = this.cache[eventName].indexOf(fn);if (index !== -1) {this.cache[eventName].splice(index, 1);}}emit(eventName) {this.cache[eventName].forEach((fn) => {fn();});}once(eventName, cb) {const one = (...args) => {cb(...args)this.off(eventName, one)}this.on(eventName,one)}
}
6、手写一个搜索的组件
支持防抖
<template><div>{{a}}<input type='text' @input="onInput()"><p>{{res}}</p></div>
</template>
<script>const fetch = () => Promise.resolve('this is fetch data')
export default{data() {return {a: '1',res: undefined}},methods: {deboundce(fn, time) {let timer;return function() {if(timer) {clearTimeout(timer)}timer = setTimeout(fn, time)}},async fetchData() {console.log(11)// return Promise.resolve(1)this.res = await fetch()},async onInput() {const fn = this.deboundce(this.fetchData, 1000)fn()// console.log(data)}}
}</script>
7、手写Promise.allSettled
Promise.allSettled 只关心所有 promise 是不是都被 settle 了,不管其是 rejected状态的 promise,还是非 rejected状态(即fulfilled)的 promise, 我都可以拿到它的最终状态并对其进行处理
Promise.allSettled 的结果数组中可能包含以下两种格式的数据
{status:"fulfilled", value:result} 对于成功的响应
{status:"rejected", reason:error} 对于 error
const promise1 = Promise.resolve(3)
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100,'foo'))
const promise3 = [promise1, promise2]Promise.myAllSettled = function(promises) {return new Promise(resolve => {const data = [], len = promises.lengthlet count = len;for(let i = 0; i < len; i++) {const promise = promises[i]promise.then(res => {data[i] = {status: 'fulfilled', value: res}},error => {data[i] = {status: 'rejected', value: error}}).finally(() => {if(!--count) {resolve(data)}})}})
}Promise.myAllSettled(promise3)
.then(results => results.forEach(result => console.log(result.status)))
8、手写bind
要支持能做为构造函数
思路:Function 的原型对象上增加一个函数,返回值是一个函数,函数的fn.prototype.constructor 指向函数和函数的 prototype 指向 Object.create(this.prototype)
const obj = {
name: 'xiao'
}function func(first,last){
console.log(first + this.name, last);}Function.prototype.myBind = function(context,...args ){
console.log(context);
context.fn = this;const fn = function() {
context.fn.apply(context,[...args])
}fn.prototype = Object.create(this.prototype)
fn.prototype.constructor = this;return fn
}
const fn1 = func.myBind(obj,'li', 'ming')
fn1()const fn2 = new fn1()console.log(fn2);
9、手写一个防抖
// 防抖 // 不管事件触发频率多高,一定在事件触发n秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,触发完事件 n 秒内不再触发事件,n秒后再执行。 // 关联记忆: 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会重新读条。
function debounce(fn, wait, immediate) {let timeout;return function () {let context = this;let args = arguments;if (timeout) clearTimeout(timeout);if (immediate) {var callNow = !timeout;timeout = setTimeout(() => {timeout = null;}, wait);if (callNow) fn.apply(context, args);} else {timeout = setTimeout(function () {fn.apply(context, args);}, wait);}};
}
10、手写一个 深拷贝
// 基础版本
function clone2=(target, map = new WeakMap()) {if (typeof target === 'object') {let cloneTarget = Array.isArray(target) ? [] : {};if(map.get(target)) return map.get(target)map.set(target, cloneTarget)for (const key in target) {cloneTarget[key] = clone(target[key], map);}return cloneTarget;} else {return target;}
};// 资深版本
function isObject (target) {const type = typeof targetreturn target !== null && (type === 'object' || type === 'function')
}function getType(target) {return Object.prototype.toString.call(target)
}function getInit(target) {const Ctor = target.constructorreturn new Ctor()
}
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]'const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const numberTag = '[object Number]';
const regexpTag = '[object RegExp]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const funcTag = '[object Function]'function forEach(array, iteratee) {let index = -1;const length = array.length;while (++index < length) {iteratee(array[index], index);}return array;
}const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]//TODO: Reg 的拷贝方法
function cloneReg(targe) {const reFlags = /\w*$/;const result = new targe.constructor(targe.source, reFlags.exec(targe));result.lastIndex = targe.lastIndex;return result;
}//TODO: Symbol 的拷贝方法
function cloneSymbol(targe) {return Object(Symbol.prototype.valueOf.call(targe));
}//TODO: cloneFunction
function cloneFunction(func) {const bodyReg = /(?<={)(.|\n)+(?=})/m;const paramReg = /(?<=().+(?=)\s+{)/;const funcString = func.toString();if (func.prototype) {console.log('普通函数');const param = paramReg.exec(funcString);const body = bodyReg.exec(funcString);if (body) {console.log('匹配到函数体:', body[0]);if (param) {const paramArr = param[0].split(',');console.log('匹配到参数:', paramArr);return new Function(...paramArr, body[0]);} else {return new Function(body[0]);}} else {return null;}} else {return eval(funcString);}
}function cloneOtherType(targe, type) {//TODO: constructorconst Ctor = targe.constructor;switch (type) {case boolTag:case numberTag:case stringTag:case errorTag:case dateTag:return new Ctor(targe);case regexpTag:return cloneReg(targe);case symbolTag:return cloneSymbol(targe);case funcTag:return cloneFunction(targe)default:return null;}
}function clone(target, map = new WeakMap()) {if(!isObject(target)) return targetconst type = getType(target)let cloneTargetif(deepTag.includes(type)) {cloneTarget = getInit(target, type)}else {return cloneOtherType(target, type)}if (map.get(target)) {return map.get(target);}map.set(target, cloneTarget);if(type === setTag) {target.forEach(val => {cloneTarget.add(clone(val, map))})return cloneTarget}// 克隆mapif (type === mapTag) {target.forEach((value, key) => {cloneTarget.set(key, clone(value,map));});return cloneTarget;}// 克隆对象和数组const keys = type === arrayTag ? undefined : Object.keys(target);forEach(keys || target, (value, key) => {if (keys) {key = value;}cloneTarget[key] = clone(target[key], map);});return cloneTarget;}