JS原型继承,盗用构造函数,组合继承,原型式继承

继承

ECMA-262 把原型链定义为 ECMAScript 的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。

原型链

重温一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型有一个constructor 属性指回构造函数,而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。

function Animal() {
 this.eat = true;
}

Animal.prototype.getAnimalProp = function () {
 return this.eat;
}

function Person() {
 this.talk = true;
}

// 继承Animal
// Person函数的原型指向 Animal的实例对象
// 重写了Person的原型
Person.prototype = new Animal();
const jiaqi = new Person();
console.log(jiaqi.getAnimalProp()); //true

这个例子中实现继承的关键,是 Person 没有使用默认原型,而是将其替换成了一个新的对象,这个对象恰巧是 Animal 的实例。

缺点:

  1. 含引用类型的原型属性会被所有实例属性共享,容易造成属性的修改混乱。
  2. 子类型在实例化时不能给父类型的构造函数传参。

盗用构造函数

基本思路很简单:在子类 构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用 apply()和 call()方法以新创建的对象为上下文执行构造函数。

这样就能解决原型包含引用值导致的继承问题。

function SuperType() {
 this.colors = ['red', 'blue', 'green'];
}


function SubType() {
 //继承superType
 SuperType.call(this);  //盗用构造函数
}

let obj1 = new SuperType();
obj1.colors.push('black');
console.log(obj1.colors); //[ 'red', 'blue', 'green', 'black' ] 

let obj2 = new SubType();
console.log(obj2.colors); //[ 'red', 'blue', 'green' ]

通过使用 call()(或 apply())方法,SuperType 构造函数在为SubType 的实例创建的新对象的上下文中执行了。这相当于新的 SubType 对象上运行了 SuperType()函数中的所有初始化代码。结果就是每个实例都会有自己的 colors 属性。

不仅可以解决原型包含引用值的问题,还能给父类构造函数传递参数。

function Person(name) {
 this.name = name;
}

  

function PersonInfo(name,age) {
 // 继承父类并传参
 Person.call(this, name);
 this.age = age;
}

const jiaqi = new PersonInfo('jiaqi', 22);
console.log(jiaqi.age,jiaqi.name); //22 jiaqi

优点:解决原型包含引用值导致的继承问题,还能给父类构造函数传递参数。

缺点: 子类不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模 式。由于存在这些问题,盗用构造函数基本上也不能单独使用。

组合继承

组合继承综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

function Animal(type) {
  this.type = type;
  this.actions = ['eat', 'move'];
}

Animal.prototype.printType = function () {
  console.log(this.type);
}

function People(name, age) {
  // 继承属性
  Animal.call(this, '人类');
  this.name = name;
  this.age = age;
}

// 继承方法
People.prototype = new Animal();

People.prototype.sayHi = function () {
  console.log(`Hi, my name is ${this.name}, I'm ${this.age}.`);
}

测试一下:

const dog = new Animal('🐶');
dog.actions.push('bark');
console.log(dog.actions); //['eat', 'move','bark']

const jiaqi = new People('嘉琪', 99);
jiaqi.sayHi(); //Hi, my name is 嘉琪, I'm 99.
jiaqi.printType(); //人类
jiaqi.actions.push('sleep');
console.log(jiaqi.actions); //[ 'eat', 'move', 'sleep' ]

组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf()方法识别合成对象的能力。

原型式继承

2006 年,Douglas Crockford 写了一篇文章:“Prototypal Inheritance in JavaScript”。这篇文章介绍了一种不涉及严格意义上构造函数的继承方法。他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。

function object(o) {
  function F() { };
  F.prototype = o;
  return new F();
}
let jiaqi = {
  name: 'jiaqi',
  hobbies:['coding','reading']
}

let anotherPerson = object(jiaqi);
anotherPerson.name = 'Jack';
anotherPerson.hobbies.push('singing');
console.log(anotherPerson.hobbies); //[ 'coding', 'reading', 'singing' ]
console.log(anotherPerson.name); //Jack
console.log(jiaqi.hobbies); //[ 'coding', 'reading', 'singing' ]

原型式继承适用于这种情况:你有一个对象,想在它的基础上再创建一个新对象。 你需要把这个对象先传给 object(),然后再对返回的对象进行适当修改。

ECMAScript 5 通过增加 Object.create()方法将原型式继承的概念规范化了。这个方法接收两个 参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。

在只有一个参数时 Object.create()和我们的object()相同。

let jiaqi = {
  name: 'jiaqi',
  hobbies:['coding','reading']
}

const anotherPerson = Object.create(jiaqi);
anotherPerson.name = 'Jack';
anotherPerson.hobbies.push('singing');
console.log(anotherPerson.hobbies); //[ 'coding', 'reading', 'singing' ]
console.log(anotherPerson.name); //Jack
console.log(jiaqi.hobbies); //[ 'coding', 'reading', 'singing' ]

Object.create()的第二个参数与 Object.defineProperties()的第二个参数一样:每个新增 属性都通过各自的描述符来描述。以这种方式添加的属性会遮蔽原型对象上的同名属性。

const anotherPerson = Object.create(jiaqi, {
  name: {
    value:'Jack'
  },
  age: {
    value: 33,
    writable: true,
    enumerable: true,
    configurable:true,
  }
})
console.log(anotherPerson); // {age:33}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值