前端并发请求控制
async function controlAsync(maxLimit, dataArray, doFn) {
let finish = [] // 完成
let doing = [] // 执行
for(let data of dataArray) {
let p = doFn(data)
finish.push(p)
if(maxLimit <= dataArray.length) {
// 如果没达到限制数就直接只往finish里添加就行
p = p.then(() => {
doing.splice(doing.indexOf(p), 1)
// 执行完成后把自己从执行列表中去除
})
doing.push(p)
if(maxLimit <= doing.length) {
await Promise.race(doing)
// 等待执行列表有完成的就可以开始下一次循环
}
}
}
return Promise.all(finish)
}
// 使用
controlAsync(
2, //参数一,同时并发的格式
[1000, 3000, 2000, 5000, 6000], //参数二,每个请求传入的参数组成的数组
(param) => //参数三,返回值被Promise包裹的异步执行函数
new Promise((reslove) => {
setTimeout(() => {
console.log(param);
reslove(param);
}, param);
})
).then((res) => {
console.log("res:", res);
});
订阅发布模式
class EventEmitter {
constructor() {
this.events = {};
}
on(eventName, Fn) {
if(this.events[eventName]) {
this.events[eventName].push(Fn);
} else {
this.events[eventName] = [];
this.events[eventName].push(Fn);
}
}
// 交牛客测试时候要去掉emit的第二个参数😂
emit(eventName, ...args) {
if(this.events[eventName]) {
this.events[eventName].forEach(item => { item(...args) });
}
}
}
手写快排
- 简单来说就是选出一个基准数,把比这个数大的放一边,比这个数小的放一边
- 然后把上述两边的部分再做同样的操作
- 一直大的一边,小的一边,到最后就能排序了
function quickSort(arr, left, right) {
if (left < right) {
// 确认两边的分界在哪
let part = sortPart(arr, left, right)
// 给两边的部分再分大小
quickSort(arr, left, part - 1)
quickSort(arr, part + 1, right)
}
return arr
}
function sortPart(arr, left, right) {
let standardNum = arr[left]
while (left < right) {
while (right > left && arr[right] > standardNum) {
right--
}
arr[left] = arr[right]
while (right > left && arr[left] <= standardNum) {
left++
}
arr[right] = arr[left]
}
arr[left] = standardNum
return left
}
观察者模式
// 被观察
class Subject {
constructor(name) {
this.name = name
this.message = ""
this.watchers = []
}
addWatcher(watcher) {
this.watchers.push(watcher)
}
setMessage(msg) {
this.message = msg
this.sendNotice()
}
sendNotice() {
this.watchers.forEach((e) => {
e.notice(this.name, this.message)
})
}
}
// 观察者
class Observer {
constructor() {
}
notice(name, message) {
console.log(`${name} changed, message:${message}`)
}
}
// 使用
const subject = new Subject("subject1");
const observerA = new Observer();
const observerB = new Observer();
subject.addWatcher(observerB);
subject.setMessage('123');
subject.addWatcher(observerA);
subject.setMessage('456');
防抖节流
防抖:n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时 节流:n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
// 防抖:
function debounce(fn, wait) {
let timer;
return function (...args) {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
// 防抖:立即执行
function debounce(fn, wait, immediate) {
let timer;
return function () {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer); // timer 不为null
if (immediate) {
let callNow = !timer;
// 无定时器的时候就立刻执行,有的话,正常走防抖逻辑
timer = setTimeout(function () {
timer = null;
}, wait)
if (callNow) {
fn.apply(context, args)
}
}
else {
timer = setTimeout(function () {
fn.apply(context, args)
}, wait);
}
}
}
// 节流:时间戳写法
function throttled(fn, delay) {
let oldTime = Date.now()
return function() {
let context = this;
let args = arguments;
let nowTime = Date.now()
// 使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行
if(nowTime - oldTime >= delay) {
fn.apply(context, args)
oldTime = Date.now()
}
}
}
// 节流:定时器写法
function throttled(fn, delay) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay);
}
}
}
// 节流:前两个结合(弥补时间戳不能停止触发后不能延迟执行)
function throttled(fn, delay) {
let timer = null
let starttime = Date.now()
return function () {
let curTime = Date.now() // 当前时间
let remaining = delay - (curTime - starttime)
// 从上一次到现在,还剩下多少多余时间
let context = this
let args = arguments
clearTimeout(timer)
if (remaining <= 0) {
fn.apply(context, args)
starttime = Date.now()
} else {
timer = setTimeout(fn, remaining);
}
}
}
深拷贝
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
// JSON.stringify()缺点: 会忽略undefined、symbol 和 函数
const obj2=JSON.parse(JSON.stringify(obj1));
深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
function deepClone(obj) {
if (obj === null) return obj;// 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj;
let cloneObj = new obj.constructor(); //保持继承链
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
};
浅拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
let oldobj = {
// ....
}
let newobj = {}
for (let k in oldobj) {
newobj[k] = oldobj[k]
}
let newobj = Object.assign({},oldobj);
promise.all
function myPromiseAll(pro) {
return new Promise((resolve, reject) => {
// 要执行的promise们
const promises = Array.from(pro)
// 结果
const result = []
let count = 0
for(let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(
res => {
// 注意保持顺序
result[i] = res;
count++;
// 都完成了就可以返回了
if(count == promises.length) {
resolve(result)
}
}
).catch(e => {
// 有一个reject就立刻返回
return reject(e)
})
}
})
}
// 测试
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(88);
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(99);
}, 3000);
});
let arr = [p1, p2, 3, 4];
myPromiseAll(arr).then(res => {
console.log(res)
})
再来个寄生组合继承吧
function Father(name) {
this.name = name
this.say = function () {
console.log("hello,world");
}
}
Father.prototype.showName = function () {
// 这个是测试输出用的
console.log(this.name)
}
function Son() {
Father.call(this, name)
this.age = age
}
Son.prototype = object.create(Father.prototype)
Son.prototype.constructor = son
手写一个 new
function myNew (Func, ...arg){
let obj = {} //定义了一个对象。
obj.__proto__ = Func.prototype //将Func.prototype赋值为对象的__proto__属性,即原型链的概念
let res = Func.call(obj, ...arg) //更改Func的this指向
return res instanceof Object ? res : obj
}
myNew(RegExp)