学习一下JS手写

55 阅读3分钟

前端并发请求控制

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)