写给自己的手写代码

900 阅读4分钟

没有生而知之,只有学而知之。多练习,多总结。给自己打打气,坚持学习,加油!欢迎大家留言补充。

实现 call

  Function.prototype.myCall = function (context) {
      const args = [...arguments].slice(1);
      //使用一个对象属性模拟
      const obj = context || window;
      const fn = Symbol("fn");
      obj[fn] = this;
      const res = obj[fn](...args);
      delete obj[fn];
      return res;
  }

实现 apply

  //同 call 不同的是第二个参数必须是数组
  Function.prototype.myApply = function (context, args = []) {
      const obj = context || window;
      const fn = Symbol("fn");
      obj[fn] = this;
      const res = obj[fn](...args);
      delete obj[fn];
      return res;
  }

实现 bind

Function.prototype.myBind = function (context, ...outerArgs) {
    let fn = this;
    function res(...innerArgs) {
        if (this instanceof res) {
            // new操作符执行时
            fn.call(this, ...outerArgs, ...innerArgs)
        } else {
            // 普通bind
            fn.call(context, ...outerArgs, ...innerArgs)
        }
    }
    res.prototype = this.prototype;
    return res
}
var foo = {
    name: "foo",
    say: function () {
        console.log(this.name);
    }
}
foo.say.myBind({name: "William"})();
function Point(x, y) {
    this.x = x;
    this.y = y;
}
Point.prototype.toString = function () {
    return `${this.x}, ${this.y}`
}
const YAxisPoint = Point.myBind(null, 0);
const tmp = new YAxisPoint(5);
console.log(tmp);
console.log(tmp instanceof YAxisPoint, tmp instanceof Point);

发布订阅

  class EventHub{
      cache = {};
      //订阅
      add(eventName, fn){
          this.cache[eventName] = this.cache[eventName] || [];
          this.cache[eventName].push(fn);
      }
      //取消订阅
      off(eventName, fn){
          const fns = this.cache[eventName] || [];
          const index = fns.indexOf(fn);
          if(index>=0){
              this.cache[eventName].splice(index, 1);
          }
      }
      //发布
      emit(eventName, data){
          const fns = this.cache[eventName] || [];
          for(let fn of fns){
              fn(data);
          }
      }
  }

实现 new

  function create(fn, ...rest) {
      //相当于 let obj.__proto__ = fn.prototype;
      let obj = Object.create(fn.prototype);
      let result = fn.apply(obj, rest);
      if (typeof result === 'object') {
          return result;
      } else {
          return obj;
      }
  }

Object.create 的基本实现原理

  //方式一
  function create(obj){
      function F(){}
      F.prototype = obj;
      return new F();
  }
  // 方式二
  function create(obj){
    const m = {};
    m.__proto__ = obj;
    return m;
  }

实现 instanceof

  function myInstanceof(f, F) {
      let flag = false;
      while (!flag && f.__proto__) {
          flag = f.__proto__ === F.prototype;
          f = f.__proto__;
      }
      return flag;
  }

实现 Array.isArray 实现

  Array.myIsArray = function(arr){
      return Object.prototype.toString.call(arr)==="[object Array]";
  }

实现继承

  function Shape(type){
      this.type = type;
  }

  Shape.prototype.draw = function(){
      console.log(this.type);
  }

  function Circle(type, color){
      //执行父级构造函数
      Shape.call(this, type);
      this.color = color;
  }
  //继承原型方法
  Circle.prototype = Object.create(Shape.prototype);
  //属性 constructor 指向本来的构造函数
  Circle.prototype.constructor = Circle;
  let c = new Circle("circle", "red");
  c.draw();

实现节流和防抖

  //节流
  function throttle(fn, delay) {
      let timer;
      return function () {
          if (!timer) {
              timer = setTimeout(() => {
                  fn();
                  timer = null;
              }, delay)
          }
      }
  }
  //防抖
  function debounce(fn, delay){
    let timer;
    return function(){
        if(timer){
            clearTimeout(timer);
        }
        timer = setTimeout(()=>{
                fn();
        }, delay);
    }
 }

实现对象(数组)深拷贝

不考虑循环引用

  function deepCopy(obj) {
      let res;
      if (typeof obj === "object") {
          if (Array.isArray(obj)) {
              res = [];
          } else {
              res = {};
          }
          for (let key in obj) {
              const tmp = obj[key];
              if (typeof tmp === "object") {
                  res[key] = deepCopy(tmp);
              } else {
                  res[key] = tmp;
              }
          }
          return res;
      } else {
          return obj;
      }
  }

考虑循环引用

上面的代码如果是循环引用对象的话,会出现 Uncaught RangeError: Maximum call stack size exceeded 递归次数过多,没有正确的退出递归造成堆栈溢出。

  function deepCopy(obj, uniqueList = []) {
      let res;
      if (typeof obj === "object") {
          res = Array.isArray(obj) ? [] : {};
          //记录所有需要递归调用的 key 和 value,避免一直循环
          const uniqueDate = find(uniqueList, obj);
          if (uniqueDate) {
              return uniqueDate.target;
          }
          uniqueList.push({
              source: obj,
              target: res
          });
          for (let key in obj) {
              const tmp = obj[key];
              if (typeof tmp === "object") {
                  res[key] = deepCopy(tmp, uniqueList);
              } else {
                  res[key] = tmp;
              }
          }
          console.log(uniqueList);
          return res;
      } else {
          return obj;
      }
  }

  function find(arr, item) {
      for (let a of arr) {
          if (a.source === item) {
              return a;
          }
      }
      return null;
  }

实现 reduce

Array.prototype.myReduce = function (fn, init=null) {
    const arr = this;
    if(arr.length===0){
        return init;
    }
    let result = null;
    let index = 0;
    if(!init){
        init = arr[index];
        index++;
    }
    while(index < arr.length){
        if(!arr.hasOwnProperty(index)){
            index++;
            continue;
        }
        const tmp = arr[index];
        result = fn(init, tmp);
        init = result;
        index++;
    }
    return result;
}

sum(1)(1,2,3)(1)

  function sum(){
      let _args = [...arguments];
      const _add = function(){
          _args = _args.concat(...arguments);
          return _add;
      }
      _add.toString = function(){
          return _args.reduce((cal, cur)=>cal+cur);
      }
      return _add();
  }

函数柯里化实现

  const curry = function(fn){
      let args=[];
      const inner = function(){
          args = args.concat(...arguments);
          if(args.length === fn.length){
              return fn(...args);
          }else{
              return inner;
          }
      }
      return inner; 
  }
  const sum = (a, b, c, d) => a + b + c + d;
  const currySum = curry(sum);
  console.log(currySum(1)(2)(3)(4));

斐波那契数列优化后

  function fabo(n){
      if(n===1||n===2){
          return 1;
      }
      let a = 1;
      let b = 1;
      for(let i=3;i<=n;i++){
          let tmp = a;
          a = b;
          b = a + tmp;
      }
      return b;
  }

具体优化过程: juejin.cn/post/691606…

实现 Promise

juejin.cn/post/691571…

实现 async & await

主要是利用 Generator 函数实现。使用 async & await 的例子:

const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000))

async function test() {
  const data = await getData()
  console.log('data: ', data);
  const data2 = await getData()
  console.log('data2: ', data2);
  return 'success'
}

// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then(res => console.log(res))

作者:ssh_晨曦时梦见兮
链接:https://juejin.cn/post/6844904102053281806

async 标志的函数会返回一个 Promise 对象,并且 await 执行的结果是 .then 里面的结果。这两点是 Generator 函数没有实现的。所以,我们使用Generator 函数模拟的时候要实现上面 2 个能力。 对于第一个问题,我们可以封装一个 asyncToGenerator 函数执行 test,让其返回 Promise 对象;对于第二个问题,使用 next(val) 将结果返回出去。

const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000));

function* test() {
    const data = yield getData();
    console.log('data: ', data);

    const data2 = yield getData();
    console.log('data2: ', data2);

    return 'success'
}
//模拟
function asyncToGenerator(generatorFunc) {
    const func = generatorFunc();
    return new Promise((resolve, reject) => {
        let g = func.next();
        function step(){
            const {done, value} = g;
            if(done){
                resolve(value);
            }else{
                return Promise.resolve(value).then(res=>{
                    g = func.next(res);
                    step();
                })
            }
        }
        step();
    })
}

asyncToGenerator(test).then(res=>{
    console.log("finish", res)
})

排序

冒泡排序

  function dubbleSort(arr) {
      for (let i = 0; i < arr.length; i++) {
          for (let j = 0; j < arr.length - i - 1; j++) {
              if (arr[j] > arr[j + 1]) {
                  let tmp = arr[j];
                  arr[j] = arr[j + 1];
                  arr[j + 1] = tmp;
              }
          }
      }
  }

时间复杂度:O(n^2)

快速排序

  function quickSort(arr){
      if(arr.length==0){
          return [];
      }
      const pirot = arr.pop();
      const left = [];
      const right = [];
      for(let m of arr){
          if(m>pirot){
              left.push(m);
          }else{
              right.push(m);
          }
      }
      return quickSort(right).concat(pirot, quickSort(left))
  }

时间复杂度:O(nlog2n)

查找

二分查找

  function binarySearch(arr, target) {
      if (arr.length === 0) {
          return -1;
      }
      let left = 0;
      let right = arr.length - 1; // [left, right]
      while (left <= right) {
          const middle = left + parseInt((right - left) / 2);
          const tmp = arr[middle];
          if (tmp === target) {
              return middle;
          } else if (tmp > target) {
              right = middle - 1;
          } else {
              left = middle + 1;
          }
      }
      return -1;
  }

JS异步调度器

  class Scheduler {
      constructor(maxNum=2) {
          this.queue = [];
          this.count = 0;
          this.maxNum = maxNum;
      }
      add(promiseCreator) {
          this.count++;
          if (this.count <= this.maxNum) {
              return promiseCreator().then(res => {
                  this.exc();
              })
          } else {
              return new Promise((resolve, reject) => {
                  this.queue.push([promiseCreator, resolve]);
              })
          }

      }
      exc() {
          if (this.queue.length > 0) {
              const [fn, resolve] = this.queue.shift();
              fn().then(res=>{
                  resolve();
                  this.exc();
              })
          }
      }
  }

  const scheduler = new Scheduler()
  const timeout = (time) => {
      return new Promise(r => setTimeout(r, time))
  }
  const start = new Date().getTime();
  const addTask = (time, order) => {
      scheduler.add(() => timeout(time))
          .then(() => console.log(order, new Date().getTime() - start))
  }
  addTask(1000, 1)
  addTask(500, 2)
  addTask(300, 3)
  addTask(400, 4)
  addTask(400, 5)

最后

如果有错误或者不严谨的地方,烦请给予指正,十分感谢。如果喜欢或者有所启发,欢迎点赞,对作者也是一种鼓励。