这阵子在整理JS的7种继承方式,发现很多文章跟视频,讲解后都不能让自己理解清晰,索性自己记录一下,希望个位发表需要修改的意见,共勉之。
部分文章总结出来的数据可能是错误的,网络上太多数据了,有些不严谨,当然我也可能是其中一人,如果有问题,欢迎提出来,共同进步。
JS的7种并不是逐级进化的(个人觉得~),可以参考下图:
Children.prototype = new Parent(); 这个是手动指向
。 这个是new 操作符里面的操作
。组合式继承 = 原型链继承 + 构造函数继承。
寄生组合式继承 = 寄生功能 + 组合式继承 = 寄生功能 + 原型链继承 + 构造函数继承。
这边回顾一下组合式继承有什么缺点,主要是实例化了父类的次数 = 子类实例化次数 + 1 (子类原型指向了父类的实例)。
其实就目前电脑的配置,这点也无关紧要,实例化一个父类能造成多大的性能损耗呢。
但还是有强迫症换成相处了解决方案,因为父类的上下文已经用构造函数继承了,只要再继承父类的原型链就好了,实现的方案就是将子类的原型指向父类的原型。
这里是原型链继承,这次
只
继承父类的原型。
父类的数据以及方法要给子类继承,通过了JS的原型链的原理,将子类的prototype,指向了实例化的父类。
Children.prototype = new Parent();
继承是实现了,但是打印对象发现结构不对,少了个构造函数,文章下方也有截图展示对比
Children.prototype.constructor = Children;
只需要将子类的原型指向父类的原型即可。
childrenFactory(Children, Parent);
父类属性:
基础数据类型
基础数据类型
复合数据类型
,需要注意,有时候误操作会影响到其他实例,因为它存放的是一个地址。主要是为了跟构造函数继承对比,构造函数的缺点就是无法使用原型链上的数据...
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>寄生组合式继承title>
head>
<body>
<h3>寄生组合式继承h3>
<h4>由于要打印原型对象,在浏览器操作比较方便,就用了html文件。h4>
<script>
// 父类
function Parent(name, age, dataA, dataB) {
console.warn("Parent 实例化了...");
// 基本数据类型
this.name = name;
this.age = age;
// 复合数据类型
this.data = { a: dataA ? dataA : "a", b: dataB ? dataB : "b", c: "c" };
// 父类内部方法
this.sayName = function () {
console.log("我的名字:", this.name);
};
// 全局的关键是看这个...原型链的bug在这里
this.sayData = function () {
// 这里还不能直接打印对象出来,因为是一个指针,总是指向最新的数据...
console.log("我的数据如下:");
console.dir(this.data.a);
console.dir(this.data.b);
console.dir(this.data.c);
console.log("我的数据结束====");
};
}
// 父类原型链方法 (这个跟原型链继承只有半毛钱关系...)
Parent.prototype.sayAge = function () {
console.log("我的年龄:", this.age);
};
// 子类,不同工种
function Children(name, age, dataA, dataB, job) {
// 原型链继承无法通过super 传递给父类,只能通过覆盖的形式了
Parent.call(this, name, age, dataA, dataB);
this.job = job;
this.sayJob = function () {
console.log("我的工作:", this.job);
};
}
// 这里不使用实例化的父类,不然会调用2次父类的构造函数。
// Children.prototype = new Parent();
// 子类已经通过构造函数继承了父类,现在只要继承父类的原型即可。
// 这边可以寄生式继承的方法,将父类原型绑定到子类即可。
function childrenFactory(Children, Parent) {
let prototype = Object.create(Parent.prototype);
prototype.constructor = Children;
Children.prototype = prototype;
}
childrenFactory(Children, Parent);
let person1 = new Children(
"钟先生",
33,
"person1A",
"person1B",
"程序员"
);
// 基本数据类型
person1.sayName();
person1.sayAge();
person1.sayJob();
// 复合数据类型
person1.sayData();
console.log("===person2...");
let person2 = new Children(
"刘小姐",
18,
"person2A",
"person2B",
"清洁阿姨"
);
// 基本数据类型
person2.sayName();
person2.sayAge();
person2.sayJob();
// 复合数据类型
person2.sayData();
console.log("===重新打印person1,不用实例化,看看person1会不会被影响...");
// 基本数据类型
person1.sayName();
person1.sayAge();
person1.sayJob();
// 复合数据类型
person1.sayData();
console.log(
"===父类构造函数只执行了2次,复合数据类型也不会混乱,很棒..."
);
script>
body>
html>
这里可以对比本博客所在专栏的组合式继承
由于Children原型链继承,只继承父类的原型,所以调用父类构造函数的次数=实例化的次数。
到这里,传统的JS继承就结束了,总结就2个BUG:
复合数据类型解决方案,只有构造函数继承,因为它是单独开辟一片内存区了。
寄生组合式继承完美解决了上述2个BUG,下一篇将讲解class继承,就是ES6才有的关键字,看看class继承与寄生组合式继承有什么区别,还是殊归同途呢。