原型链以及call/bind的手撕

139 阅读3分钟

image.png

  • 构造函数有prototype指向构造函数的原型
  • 原型有constructor指向将此对象作为原型的构造函数
  • 对象有_prototype_指向对象实例的原型
  • 原型也是一个实例对象可以有链的下一节点,底部节点为Object构造与object原型

一些问题

  • 方法定义在构造函数中,每个实例有单独的构造方法,在内存中是独立的。方法定义在原型上,调用的是原型的方法,同一块内存,==值为true。
  • __proto__ 是非标准属性,如果要访问一个对象的原型,建议使用 ES6 新增的 Reflect.getPrototypeOf 或者 Object.getPrototypeOf() 方法,而不是直接 obj.__proto__,因为非标准属性意味着未来可能直接会修改或者移除该属性。同理,当改变一个对象的原型时,最好也使用 ES6 提供的 Reflect.setPrototypeOfObject.setPrototypeOf
let target = {};
let newProto = {};
Reflect.getPrototypeOf(target) === newProto; // false
Reflect.setPrototypeOf(target, newProto);
Reflect.getPrototypeOf(target) === newProto; // true
复制代码
  • 函数都会有 prototype ,除了 Function.prototype.bind() 之外。
  • 对象都会有 __proto__ ,除了 Object.prototype 之外(其实它也是有的,之不过是 null)。
  • 所有函数都由 Function 创建而来,也就是说他们的 __proto__ 都等于 Function.prototype
  • Function.prototype 等于 Function.__proto__

构造函数

  • 箭头函数不可用作构造函数,因为没有自己的this指针,值来源于上下文继承值。 构造函数普通调用会把对象原型和特征值方法等赋给最近的this或window

New 操作符

new操作符的工作流程

  • 创建一个空对象
  • __prototype__指向构造函数的prototype
  • 改变this指向到新对象
  • 如果构造函数没有返回对象,那么new自动返回第一步的这个新对象
function myNew(Creater,...args){
    //判断
    if (typeof Creater !== 'function') return new Error('type err');
    //生成新对象并继承
    let obj = {};
    let res = Creater.call(obj,...args);
    obj.__proto__ = Object.create(Creater.prototype);
    // Object.setPrototypeOf(obj,Creater.prototype);官方MDN表示性能差不推荐
    obj.constructor = Creater;
    //判断函数本身的返回是否为引用类型
    let isObject = typeof res === 'object' && res !== null;
    let isFunction = typeof res === 'function';
    return (isFunction || isObject)? res:obj;
}

es5继承

继承!! - 掘金 (juejin.cn)

call apply bind

call/apply

  • 判断是否一个参数都每,如果没有则把主体设为window
  • 生成唯一值作为主题临时函数的函数名防止冲突
  • 把this函数绑定在主体上
  • 执行并保存结果
  • 删除主体的函数并返回结果
  • call与apply区别是一个是扩展参一个是数组
Function.prototype.myCall = function (obj,...arr){
  let myObj = obj ? Object(obj) : window;
  let key = Symbol('soga');
  myObj[key] = this;
  let res = myObj[key](...arr);
  delete myObj[key];
  return res;
}

bind

  • 判断this不是函数抛出异常
  • 定义this变量fn在后面的function里用
  • 定义要返回的结果函数,参数为扩展数组
  • 结果函数为用新的obj执行this,但是如果使用了new(调用时候的this是当前函数的原型构造来的)则就使用new出来的this。第二个参数为柯里化两部分参数的链接。
  • 使结果函数的原型指向当前函数的原型,防止后续操作污染当前函数原型添加一层空的函数层来链传递指向。
  • 返回结果
Function.prototype.myBind = function (obj,...arr){

  if (typeof this !== 'function'){
    throw new Error("not a function!");
  }

  let fn = this;
  let res = function (...arr2){
  fn.apply(this instanceof fn? this : obj,[...arr,...arr2]);
}

res.prototype = Object.create(fn.prototype);

  return res;
}