前端手写各种(二)

285 阅读2分钟

因为现在大环境的原因,前端对底层原理需要有一定的掌握,所以特地学习并记录一下前端涉及到的各种手写及算法。 书接上文前端手写各种(一)

5.手写 new 操作符的实现

function myNew(fn, ...args) {
  // 创建一个新对象(若该函数不是 JS 内置的,则创建一个新的 Object 对象)
  let obj = Object.create(fn.prototype);
  // 将 this 绑定到这个对象;执行构造函数中的代码(为这个新对象添加属性)
  let res = fn.call(obj, ...args);
  //若函数没有返回其他对象,则自动返回这个新对象;若函数有 return 返回的是非对象,则还是自动返回这个新对象,即覆盖那个非对象。
  if (res && (typeof res === "object" || typeof res === "function")) {
    return res;
  }
  return obj;
}

6.手写 call、apply、bind

Function.prototype.myCall = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值,作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this; // this指向调用call的函数
  // 执行函数并返回结果,相当于把自身作为传入的context的方法进行调用了
  return context[fn](...args);
};

// apply原理一致,只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值,作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this;
  // 执行函数并返回结果
  return context[fn](...args);
};

// bind实现较为复杂
Function.prototype.myBind = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // 创造唯一的key值,作为我们构造的context内部方法名
  let fn = Symbol();
  context[fn] = this;
  let _this = this;
  // bind情况较为复杂
  const result = function (...innerArgs) {
    if (this instanceof _this === true) {
      // 此时this指向指向result的实例  这时候不需要改变this指向
      this[fn] = _this;
      this[fn](...[...args, ...innerArgs]); //这里使用es6的方法让bind支持参数合并
    } else {
      // 如果只是作为普通函数调用  那就很简单了 直接改变this指向为传入的context
      context[fn](...[...args, ...innerArgs]);
    }
    // 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
    // 实现继承的方式: 使用Object.create
    result.prototype = Object.create(this.prototype);
    return result;
  };
};

感兴趣的童鞋,可以继续关注后续文章。