一个问题引出JS原型与原型链的处理机制

156 阅读3分钟

「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」。

一个问题引出JS原型与原型链的处理机制

function Fn(x, y) {
    let sum = 10;
    this.total = x + y;
    this.say = function () {
        console.log(`我计算的和是:${this.total}`);
    };
}
let f1 = new Fn(10, 20); 
console.log(f1.sum);
console.log(f1.total);
  • 上面代码,检测某个属性是否为对象的'私有属性'
    • f1是Fn类的实例,也是Object这个类的实例
    • Object.prototype.hasOwnProperty 这个方法就是用来检测私有属性的
     console.log(f1.hasOwnProperty('say')); //=>true 特点:必须是它的一个私有属性才可以「有这个属性,但是不是私有的不行 & 没有这个属性更不行」
     console.log(f1.hasOwnProperty('hasOwnProperty')); //=>false
    
  • 只想检测是不是他的一个属性[不论是私有还是公有]
     console.log('say' in f1); //=>true
     console.log('hasOwnProperty' in f1); //=>true
     console.log('sum' in f1); //=>false
    
  • 需求:检测这个属性是否为他的公有属性? 方案 -> 是它的属性,但还不能是私有的
function hasPubProperty(obj, attr) {
    return (attr in obj) && !obj.hasOwnProperty(attr);
}
console.log(hasPubProperty(f1, 'say')); //=>false
console.log(hasPubProperty(f1, 'hasOwnProperty')); //=>true
console.log(hasPubProperty(f1, 'sum')); //=>false

// toString既是私有的,也是公有的
f1.toString = function () {};
console.log(hasPubProperty(f1, 'toString')); //=>false  ? */
//对于toString这个属性来说,该如何检测呢?我们先来引出JS中原型与原型链的机制再来解决这个问题

/* // instanceof:检测当前实例是否为某个类的实例
console.log(f1 instanceof Fn); //=>true
console.log(f1 instanceof Object); //=>true
console.log(f1 instanceof Array); //=>false */
  • JS中的原型与原型链处理机制
    • prototype

    大部分“函数数据类型”的值都具备“prototype(原型/显式原型)”属性,属性值本身是一个对象「浏览器会默认为其开辟一个堆内存,用来存储实例可调用的公共的属性和方法」,在浏览器默认开辟的这个堆内存中「原型对象」有一个默认的属性“constructor(构造函数/构造器)”,属性值是当前函数/类本身!!

    • 函数数据类型
      • 普通函数(实名或者匿名函数)
      • 箭头函数
      • 构造函数/类「内置类/自定义类」
      • 生成器函数 Generator
      • ...
    • 不具备prototype的函数
      • 箭头函数
      • 基于ES6给对象某个成员赋值函数值的快捷操作
      • ...
    • __proto__

    每一个“对象数据类型”的值都具备一个属性“__proto__(原型链/隐式原型)”,属性值指向“自己所属类的原型(prototype)”

    • 对象数据类型值
      • 普通对象
      • 特殊对象:数组、正则、日期、Math、Error…
      • 函数对象
      • 实例对象
      • 构造函数.prototype
  • 以下代码作为运行示例
function Fn() {
    this.x = 100;
    this.y = 200;
    this.getX = function () {
        console.log(this.x);
    }
}
Fn.prototype.getX = function () {
    console.log(this.x);
};
Fn.prototype.getY = function () {
    console.log(this.y);
};
let f1 = new Fn;
let f2 = new Fn;
console.log(f1.getX === f2.getX);
console.log(f1.getY === f2.getY);
console.log(f1.__proto__.getY === Fn.prototype.getY);
console.log(f1.__proto__.getX === f2.getX);
console.log(f1.getX === Fn.prototype.getX);
console.log(f1.constructor);
console.log(Fn.prototype.__proto__.constructor);
f1.getX();
f1.__proto__.getX();
f2.getY();
Fn.prototype.getY();

原型与原型链.png

  • Function与Object在原型链中的关系

Function与Object在原型链中的关系.png

  • 需求: obj之前如果是数组,我们则创建一个新的数组;如果是正则,则创建一个新的正则;如果是对象,则创建一个新的对象;..
    • obj.constructor正常情况下,获取到的都是obj实列对象所属的类
    • let obj2 = new obj.constructor obj2即为所需对象
  • eg:代码分析
  function C1(name) {
      if (name) {
          this.name = name;
      }
  }
  function C2(name) {
      this.name = name;
  }
  function C3(name) {
      this.name = name || 'join';
  }
  C1.prototype.name = 'Tom';
  C2.prototype.name = 'Tom';
  C3.prototype.name = 'Tom';
  alert((new C1().name) + (new C2().name) + (new C3().name));
  // 'Tom' + undefined + 'join'  => 'Tomundefinedjoin' 
  • 返回到上面检测公有属性的问题
    • 例子:hasPubProperty 不管私有是否存在,我们只看公有中是否有,只要公有中有,则当前属性就是对象的公有属性
    • Object.getPrototypeOf(obj) 获取某个对象(实例)的原型:__proto__指向的原型对象
Object.prototype.hasPubProperty = function hasPubProperty(attr){
  // this -> obj 要处理的对象
  // 找到当前对象的原型,而且一直向上找,直到找到Object.prototype为止;在查找过程中,只要某个原型对象中有ATTR这个属性,则证明这个属性就是对象的公有属性...
  let proto = Object.getPrototypeOf(this)
  while(proto){
    if(proto.hasOwnProperty(attr)) return true
    proto = Object.getPrototypeOf(proto)
  }
  return false
}
let obj = {
  name:'obj',
  toString(){}
}
obj.hasPubProperty('name') // => false
obj.hasPubProperty('toString') // => true