6

JS必知的6种继承方式

 3 years ago
source link: https://my.oschina.net/jack088/blog/4869483
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一。那么如何在JS中实现继承呢?让我们拭目以待

JS继承的实现方式

既然要实现继承,那么首先我们得有一个父类,代码如下:

// 父类
function Person(name) { // 给构造函数添加了参数
  this.name = name;
  this.sum = function() {
    alert(this.name)
  }
}
Person.prototype.age = 10; // 给构造函数添加了原型属性

1、原型链继承

// 原型链继承
function Per() {
  this.name = "ker";
}
Per.prototype = new Person(); // 主要
var per1 = new Per();
console.log(per1.age); // 10
// instanceof 判断元素是否在另一个元素的原型链上
// per1 继承了Person的属性,返回true
console.log(per1 instanceof Person); // true

重点:让新实例的原型等于父类的实例 特点:

  1. 实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!) 缺点:

  2. 新实例无法向父类构造函数传参

  3. 所有新实例都会共享父类实例的属性(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)

2、借用构造函数继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

funciton Con () {
  Person.call(this, "jer"); // 重点
  this.age = 12;
}
var con1 = new Con();
console.log(con1.name); // "jer"
console.log(con1.age); // "12"
console.log(con1 instanceof Person); // false

重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制)) 特点:

  1. 只继承了父类构造函数的属性,没有继承父类原型的属性

  2. 解决了原型链继承缺点1,2,3

  3. 可以继承多个构造函数属性(call多个)

  4. 在子实例中可向父实例传参 缺点:

  5. 只能继承父类构造函数的属性

  6. 无法实现构造函数的服用(每次用每次都要重新调用)

  7. 每个新实例都有父类构造函数的副本,臃肿

3、组合继承(组合原型链继承和借用构造函数继承)(常用)

// 组合原型链和构造函数继承
function SubType (name) {
  Person.call(this, name); // 借用构造函数模式
}
SubType.prototype = new Person(); // 原型链继承
var sub = new SubType("gar");
console.log(sub.name); // "gar"继承了构造函数属性
console.log(sub.age); // 10继承了父类原型的属性

重点:结合了两种模式的优点,传参和复用 特点:

  1. 可以继承父类原型上的属性,可以传参,可复用

  2. 每个新实例引入的构造函数属性是私有的 缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数

4、原型式继承

// 先封装一个函数容器,用来输出对象和承载继承的原型
function content(obj) {
  function F() {}
  F.prototype = obj; // 继承了传入的参数
  return new F(); // 返回函数对象
}
var sup = new Person(); // 拿到父类的实例
var sup1 = content(sup);
console.log(sup1.age); // 10 继承了父类函数的属性

重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。Object.create()就是这个原理 特点:类似于复制一个对象,用函数来包装 缺点:

  1. 所有实例都会继承原型上的属性

  2. 无法实现复用(新实例属性都是后面添加的)

5、寄生式继承

function content(obj) {
  function F() {}
  F.prototype = obj; // 继承了传入的参数
  return new F(); // 返回函数对象
}
var sup = new Person();
// 这个函数经过声明之后就成了可增添属性的对象
console.log(typeof subobject); // function
console.log(typeof sup2); // object
console.log(sup2.name); // "gar",返回了个sub对象,继承了sub属性

重点:就是给原型式继承外面套了个壳子 特点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺利成章就成了创建的新对象 缺点:没有到原型,无法复用

6、寄生组合式继承(常用)

寄生:在函数内返回对象然后调用 组合:

  1. 函数的原型等于另一个实例

  2. 在函数中用apply或者calL引入另一个构造函数,可传参

// 寄生
function content(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}
// content就是F实例的另一个表示法
var con = content(Person.prototype);
// con实例(F实例)的原型继承了父类函数的原型
// 上述更像是原型链继承,只不过只继承了原型属性

// 组合
function Sub() {
  Person.all(this); // 这个继承了父类构造函数的属性// 解决了组合式两次调用构造函数属性的缺点
// 重点
Sub.prototype = con; // 继承了con实例
con.constructor = Sub; // 一定要修复实例
var sub1 = new Sub();
// Sub的实例就继承了构造函数属性,父类实例,con的函数属性
console.log(sub1.age); // 10;

重点:修复了组合继承的问题

继承这些知识点与其说是对象的继承,更像是函数的功能用法,如何用函数做到复用,组合,这些和使用继承的思考是一样的,上述几个继承的方法都可以手动修复他们的缺点,但就是多了这个手动修复就变成了另一种继承模式。这些继承模式的学习重点是学它们的思想,不然你会在coding书本上的例子的时候,会觉得明明可以直接继承为什么还要搞这么麻烦。就像原型式继承它用函数复制了内部对象的一个副本,这样不仅可以继承内部对象的属性,还能把函数(对象,来源内部对象的返回)随意调用,给它们添加属性,改个参数就可以改变原型对象,而这些新增的属性也不会相互影响。

https://www.cnblogs.com/ranyonsue/p/11201730.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK