JS - 原型式继承及其规范化

75 阅读1分钟

原型式继承适用一个对象的基础上再创建一个新对象,然后通过修改这个对象来实现继承

笔者认为: 原型式继承和原型链继承本质一样,都是通过重写一个构造函数的 prototype 并为 prototype 来指向一个实例,因此原型链继承有的问题,如果没有重写继承的属性,原型式它也有(如果错了欢迎指正,3ks)

function Inherit(obj) {
  // 创建一个临时构造函数,
  function F() {}
  // 笔者看来,原型式继承和原型链继承本质相等,这里的 F 就可以理解为 子构造函数,obj 就是父元素的实例
  // 将 F 的 原型 指向这个对象(将传入的对象赋值给这个构造函数的原型,对对象进行一次浅复制)
  F.prototype = obj;

  return new F();
}

const obj = {
  name: "obj_name",
  friends: ["Yohan", "Wendeer"],
  sayName() {
    log(this.name);
  },
};

const i1 = Inherit(obj);
i1.name = "i1_name";
i1.sayName(); // log: i1_name

i1.friends.push("Zhuli"); // 更改了原型上的 friends,其他实例也跟着变化

const i2 = Inherit(obj);
i2.name = "i2_name";
i2.sayName(); // i2_name
log(i2.friends); // log: [ 'Yohan', 'Wendeer', 'Zhuli' ] 被迫新增了一个朋友

Object.create 原型式继承规范化

ts 声明: create(o: object | null, properties?: PropertyDescriptorMap & ThisType): any;

// // ts 第二个参数 键对应的配置 类型
// interface PropertyDescriptor {
//     configurable?: boolean;
//     enumerable?: boolean;
//     value?: any;
//     writable?: boolean;
//     get?(): any;
//     set?(v: any): void;
// }

// Object.create 将原型是规范化了
const parent = {
  name: "parent_name",
  friends: ["Wendeer", "Yohan"],
  say() {
    log(this.name);
  },
};

const sonObj1 = Object.create(parent, {
  name: {
    value: "son_name",
  },
});

sonObj1.friends.push("Zhuli");
sonObj1.say(); //log: son_name

const sonObj2 = Object.create(parent);
// 虽然规范化了,但是共享实例属性造成的问题仍然存在,除非在 Object.create 的第二个参数完全重写对应的 描述符配置
log(sonObj2.friends); // log: [ 'Wendeer', 'Yohan', 'Zhuli' ];  sonObj2 的朋友也被迫改变