你不知道的JS(八):原型与继承

书中原文讨论比较深,这里只总结几个点。

  • 原型机制其实就是关联对象,而前面介绍了几种方法,可以用来创建关联对象,但是都是一个。
  • 如果是需要一个原型对象用来创建多个实例,而这个原型对象又需要与已有的原型对象关联,那么就需要下面的方式。

原型继承

在这里插入图片描述

  • 图中b1可以继承Foo.prototype,也就是访问Foo.prototype包含的属性。
  • 利用这种关系,可以实现两个构造函数的继承:
  • 通过子类继承父类,也就是通过两个构造函数建立起联系。子类的实例可以访问父类原型上的属性。

代码实现:

function Father(){
	this.property = true;
}
Father.prototype.getFatherValue = function(){
	return this.property;
}
function Son(){
	this.sonProperty = false;
}
//继承 Father
Son.prototype = new Father();//Son.prototype被重写,导致Son.prototype.constructor也一同被重写
Son.prototype.getSonVaule = function(){
	return this.sonProperty;
}
var instance = new Son();
alert(instance.getFatherValue());//true
  • 前面我们说过原型直接赋值会互相影响的问题,这里得到解决。
  • 这是可能是比较常见的写法。但是,这种写法会有一个问题,就是new操作过程会有副作用,所以需要改为Object.create()
// 直接引用,不是需要的结果,否则不需要Son
Son.prototype  = Father.prototype
// 有副作用
Son.prototype = new Father();

// 修改为
Son.prototype = Object.create(Father.prototype);

当然,这里也可以用Object.setPrototypeOf()
后面的new Son()也同样进行修改,避免new操作。

  • 我的理解是,原型链继承其实找了个中间的人。
  • 从上面这段代码中,Son()Father()都是构造函数,目的是让Son的实例对象能访问到Father的原型对象。这里的中间人就是Father的实例对象。
  • 所以Son.prototype = Object.create(Father.prototype),分成两段来看就是:中间人 = Object.create(Father.prototype);Son.prototype = 中间人

缺点

  • 无法传参

    子类型实例不能给父类型构造函数传参

  • 引用类型属性共享

    父类的所有引用类型的属性会被所有子类共享,更改一个子类的引用类型的属性值,其他子类也会受影响

借用构造函数继承

这实际上是new 操作和 call 方法结合实现的继承。

// 父类构造函数
function Parent() {
  this.info = {
    name: "yhd",
    age: 19,
  }
}
// 子类构造函数
function Child() {
	// call方法调用父类
    Parent.call(this)
}
// 创建子类实例
let child1 = new Child();
// 使用父类属性和方法
child1.info.gender = "男";
console.log(child1.info); // {name: "yhd", age: 19, gender: "男"};

let child2 = new Child();
console.log(child2.info); // {name: "yhd", age: 19}

大致过程

  • 相当于新建一个对象(实例),将__proto__赋值为子类的原型对象,子类作为它的方法执行。
  • 子类执行的过程中会执行Parent.call(this),这里的this指向新建的实例对象。
  • 在call方法里面,将执行this.Parent(),也就是将Parent(),作为this的方法进行调用。
  • 所以最终是将Parent()作为Child()的实例对象的方法执行,为这个实例对象添加属性和方法,并不是真正意义上的继承。

优点

​ 解决了原型链继承的引用类型的问题

缺点

  • 占用内存

    每个实例都拷贝一份父类的方法,占用内存大,尤其是方法过多的时候。

  • 无法及时更新

    而且该方法都不能及时作出更新

本质上来说,这里的继承其实是在函数执行的过程中发生的。并不是一直存在的联系。

组合继承

其实就是将原型链继承和借用构造函数继承结合起来。

思路

  • 前面说了,每个实例都拷贝一份父类的方法,占用内存大,那把方法放到原型链上就可以了,通过原型链继承都共享同一个方法。
  • 还有原型链的引用类型共享问题,将引用类型(除了函数)都放到构造函数中,通过执行构造函数,每个实例在生成自己的属性,避免了互相干扰。

特点

​ 把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性

代码实现

function Parent(name) {
   this.name = name
   this.colors = ["red", "blue", "yellow"]
}
Parent.prototype.sayName = function () {
   console.log(this.name);
}

function Child(name, age) {
   // 继承父类属性
   Parent.call(this, name)
   this.age = age;
}
// 继承父类方法
Child.prototype = new Parent();

Child.prototype.sayAge = function () {
   console.log(this.age);
}

let child1 = new Child("yhd", 19);
child1.colors.push("pink");
console.log(child1.colors); // ["red", "blue", "yellow", "pink"]
child1.sayAge(); // 19
child1.sayName(); // "yhd"

let child2 = new Child("wxb", 30);
console.log(child2.colors);  // ["red", "blue", "yellow"]
child2.sayAge(); // 30
child2.sayName(); // "wxb"

这里就基本解决了上面的问题。

但是又有一个问题,就是需要进行new操作,会带来副作用。
而且这里的new操作不能改为Object.creact(),因为对象的属性就是在执行构造函数的过程种产生的,改为Object.creact()就失去这个作用。

ES6的类

​ 在定义函数的时候使用extends父类,然后在构造函数中调用super方法继承了父类的所有属性和方法(实际是组合继承)

ES5代码实现:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

转化为ES6的类:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

// 正确
var point = new Point(2, 3);

其实JS语法中的类只是原型机制的语法糖,只是对代码内容进行简化。

实际上没有真正的类,class仍然是通过原型机制实现的。

传统的类在声明时静态复制所有行为,而ES6的class并不是。如果修改了父类的方法,会影响到所有子类和实例。

提升:类不存在提升。

静态方法:如果在一个方法前面加static,那么该方法不会被实例继承,而是直接通过类来调用。

子类继承:使用extends和super

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值