常见底层函数实现

225 阅读2分钟

前端面试中常常要求手写底层函数实现, 现在总结了一些,拿走不谢,并持续更新中...

1. new 关键字

  • 思路: 改变this指向 + 原型继承

    // new关键字使用
     function Father(name) {
       this.name = name;
     }
     Father.prototype.getName = function() {
       return this.name;
     };
     let child = new Father("ming");
     // console.log("name", child.name);
     // console.log("name", child.getName());
    
    // 关键字实现
     function newFn(fn) {
       const obj = {};
       obj.__proto__ = fn.prototype; // 原型继承
       fn.apply(obj, [...arguments].slice(1)); // 改变构造函数this指向
       return obj;
     }
     function Fn(name) {
       this.name = name;
     }
     let exampleObj = newFn(Fn, "jojo");
     // console.log("name", exampleObj.name);
    

2. instanceof 实现

  • 判断对象是否在构造函数的原型链上
    function instanceofFn(obj, Fn) {
      let objProto = obj.__proto__; // 对象的__proto__
      let Fnprototype = Fn.prototype; // 构造函数的原型
      while (objProto !== null) { // 如果__proto__为null, 跳出循环
        if ((objProto === Fnprototype)) {
          return true;
        }
        objProto = objProto.__proto__;
      }
      return false;
    }
    // console.log("instance", instanceofFn({}, Object));
    // console.log("instance", instanceofFn(Function, Object));
    // console.log("instance", instanceofFn(instanceFn, Object));

3. call, apply, bind 函数实现

// call的使用
    function fnCall() {
      console.log(this.name);
      console.log(arguments);
    }
    let callObj = {
      name: "jojo"
    };
    // fnCall();
    // fnCall.call(callObj);

// call函数实现
Function.prototype.myCall = function(context, ...args) {
      context = typeof context === "object" ? context : window; // 如果是对象, this指向为当前对象, 如果不是指向window
      const key = Symbol(); // key值唯一
      context[key] = this;
      context[key](...args); // 函数执行, 传参进去
      delete context[key]; // 删除函数
    };
    // fnCall.myCall(callObj, 1, 2);
    // apply函数实现 
    // 唯一区别在于传参
    Function.prototype.apply = function(context) {
      context = typeof context === "object" ? context : window;
      const key = Symbol();
      context[key] = this;
      context[key]([...arguments].slice(1));
      delete context[key];
    };
// bind 函数实现
// 绑定对象且返回一个函数, 可接受传参
    Function.prototype.myBind = function(context) {
      context = typeof context === "object" ? context : window;
      return (...args) => {
        // 箭头函数内部没有arguments
        this.call(context, ...args);
      };
    };
    // fnCall.myBind(callObj)(1);

4. Object.create函数实现

  • 创建一个对象, 新对象可以使用原来对象的属性和方法
    function objectCreate(obj) {
      function M() {}
      M.prototype = obj; // 构造函数的原型指向原来的对象
      return new M(); // 返回新对象
    }
    const obj2 = {
      name: "jojo",
      age: "18",
      getName() {
        console.log(this.name);
      }
    };
    Object.prototype.getAge = function() {
      console.log(1);
    };
    let objCreate = objectCreate(obj2);
    objCreate.getName();
    objCreate.getAge();

5. 函数科里化

  • 闭包 + 递归

固定参数 sum(1,2,3)

// 常见面试题sum(1,2,3) sum(1,2)(3) sum(1)(2)(3) 等于6
    function totalSum(a, b, c) {
      return a + b + c;
    }
    function currySum(fn) {
      const arg = [...arguments].slice(1); // 存储私有变量
      const length = fn.length;

      return function() {
        const totalArgs = arg.concat([...arguments]);

        if (totalArgs.length < length) {
          return currySum.call(this, fn, ...totalArgs); // 如果totalArgs长度不等于函数的参数, 递归
        } else {
          return fn.apply(this, totalArgs);
        }
      };
    }
    let total = currySum(totalSum);
    // total(2)(3)(4);

不定参数 sum(1,2)(3)(4)(5)...

    function curry() {
      let args = [...arguments] || [];

      let fn = function() {
        let totalArgs = args.concat([...arguments]);
        return curry.apply(this, totalArgs);
      };
      // toString 隐式转换
      fn.toString = function() {
        return args.reduce(function(a, b) {
          return a + b;
        });
      };

      return fn;
    }
    curry(1)(2)(3, 4)(5).toString(); // 要调用toString() 方法

6. 深拷贝

function deepclone(target, map = new WeakMap()){
    if(typeof(target) === 'object'){
        const cloneTarget = Array.isArray(target) ? [] : {}
        if(map.get(target)){
            return map.get(target)
        }
        map.set(target, cloneTarget)
        for(const key in target){
            cloneTarget[key] = deepclone(target[key], map)
        }

        return cloneTarget
    }else{
        return target
    }
}

let obj = {
    a:1,
    b:{
        c:{
            d:33
        }
    }
}
let obj1 = deepclone(obj)
  1. flat
[1,[2,3,4,[5,6]]] => [1,2,3,4,5,6]


Array.prototype.flat = function(){
this.toString().split(",").map(item => +item)
}