这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
前言
上期我们大概讲了一些基本的东西,比如什么是继承,原型链的检索规则,以及对继承举了2个例子啊
今天我们继续举例,借助代码和图解进一步学习,然后再进行一个小总结。
一、继承-原型链继承-2
这里我们解决昨天遗留的那个问题先。
问题:无法访问到父类的对象属性?
换句话来说就是怎样才能拥有父类的实例属性和原型属性?
解决方案:构造父类的实例,并设置为子类的原型对象
<script>
/**
* 构造函数Person
* @constructor
*/
function Person() {
this.name = 'Jack';
this.lore = ['html', 'css'];
}
Person.prototype.run = function () {
console.log('跑');
};
/**
* 构造函数Student
* @constructor
*/
function Student() {
this.num = 'Tom';
}
// 1. 构造父类的实例
var p = new Person();
// 2. 并设置为子类的原型对象
Student.prototype = p;
var stu = new Student();
console.log(stu); // Student {num: "Tom"}
console.log(stu.num); // Tom
stu.run(); // 跑
console.log(stu.name); // Jack
console.log(stu.lore); // ["html", "css"]
console.log(stu.constructor.name); // Person
</script>
复制代码
图解
问题:类型问题
二、继承-原型链继承-3
解决问题:修复constructor指针即可
<script>
/**
* 构造函数Person
* @constructor
*/
function Person() {
this.name = 'Jack';
this.lore = ['html', 'css'];
}
Person.prototype.run = function () {
console.log('跑');
};
/**
* 构造函数Student
* @constructor
*/
function Student() {
this.num = 'Tom';
}
// 1. 构造父类的实例
var p = new Person();
// 2. 并设置为子类的原型对象
Student.prototype = p;
// 3.修复constructor指针即可
Student.prototype.constructor = Student;
var stu = new Student();
console.log(stu); // Student {num: "Tom"}
console.log(stu.num); // Tom
stu.run(); // 跑
console.log(stu.name); // Jack
console.log(stu.lore); // ["html", "css"]
console.log(stu.constructor.name); // Student
</script>
复制代码
图解
问题:继承过来的实例属性, 如果是引用类型, 会被多个子类的实例共享
注意:到此为止,原型链继承已经结束
三、继承-原型链继承-4
思路: 只要在原型对象上,肯定都会被共享。
解决方案
1. 添加到对象自己身上
(1) 在创建过对象后添加(复用性差,冗余度高,稳定性差,不采用),不推荐。
(2) 在构造函数内部添加
function Stu () {
this.num = '20210819';
// 必须跟父类写的一模一样
this.name = 'Rose';
this.lore = ['js', 'jquery'];
}
复制代码
优化: 可以直接调用父类构造函数, 但是需要修改this指向
function Stu() {
this.num = '20210819';
// 必须跟父类写的一模一样
// this.name = 'Rose';
// this.lore = ['js', 'jquery'];
Person.call(this);
}
复制代码
再优化: 注意覆盖关系,如果产生重名, 应该, 子类覆盖父类
function Stu() {
Person.call(this);
this.num = '20210819';
// 必须跟父类写的一模一样
// this.name = 'Rose';
// this.lore = ['js', 'jquery'];
}
复制代码
图解
Student函数就已经获得了Person函数的name和lore了。
概念:“借用构造函数继承”,在子构造函数内部,调用父构造函数
注意: 从此处开始,已经涵盖了原型链继承和借助构造函数继承,称之为 “组合继承” = 原型链 + 借助构造函数
问题: 父类构造函数的参数无法修改
四、继承-原型链继承-5
解决方案
- 父类构造函数, 需要设置接收可变参数
- 子类构造函数在调用父类构造函数的时候, 传递参数即可
<script>
/**
* 构造函数Person
* @constructor
*/
function Person(name, lore) {
this.name = name;
this.lore = lore;
}
Person.prototype.run = function () {
console.log('跑');
};
/**
* 构造函数Student
* @constructor
*/
function Student(num, name, lore) {
// 注意: 一定要放在最前面
Person.call(this, name, lore);
this.num = num;
}
// 1. 构造父类的实例
var p = new Person();
// 2. 并设置为子类的原型对象
Student.prototype = p;
// 3.修复constructor指针即可
Student.prototype.constructor = Student;
var stu = new Student('001', '张三', ['html']);
var stu2 = new Student('002', '李四', ['css']);
console.log(stu);
console.log(stu2);
</script>
复制代码
问题: 父类属性重复,实例上有一份,原型对象上有一份。(如下图)
五、继承-原型链继承-6
思路
1. 理解为什么会有两份?
因为调用了两次父类构造函数
- 1次,在子类构造函数内部
- 2次,创建子类构造函数原型对象时
分析
两次调用的意义?
- 1次, 在子类构造函数内部
- 为了获取父类的实例属性
- 2次, 创建子类构造函数原型对象时
- 为了获取父类的实例属性
- 为了获取实例的原型对象属性
结论: 想办法, 设置子类构造函数的原型对象时, 只要父类构造函数的原型对象属性
解决方案
方案1:
修改子类构造函数的原型对象指针, 为父类构造函数的原型对象
问题:
- 共享原型对象
- 容易引发冲突
- 无法判定类型
不采用!!!
方案2:
思路:
能不能修改子类构造函数的原型对象指针, 为一个对象
这个对象, 只能访问到父类原型对象的属性/方法?
解决方案
- 拷贝父类构造函数的原型对象
- 注意: 此处是指拷贝原型对象的内容, 不是整个地址过来
- 后面会讲解如何拷贝, 到时自行实现这块
- 借助父类构造函数的原型对象, 创建出来一个空对象实例
图解
概念
1. 原型式继承
借助原型,然后基于已有的对象, 创建出新对象;同时不需要创建自定义类型
核心
function createObjWithObj(obj) {
function Tmp() {}
Tmp.prototype = obj;
var o = new Tmp();
return o;
}
复制代码
系统实现
Object.create
1. 作用
- 创建对象,并且设置该对象的原型对象为传递过来的参数
- 兼容ES5,IE8不支持
2. 简单用法
var obj = {name:"sz"};
var obj2 = Object.create(obj);
// 创建了一个空对象obj2
// 把obj, 作为obj2的原型对象
复制代码
3. 兼容处理
if(typeof Object.create == "function") {
var o = Object.create(obj);
} else {
Object.create = function(){
function F(){};
F.prototype = obj;
var o = new F();
}
}
复制代码
2. 寄生式继承
在原型式基础上增强这个对象,所谓增加, 就是指, 再次给这个对象增加一些属性或者方法
核心
function createNewObjWithObj(obj) {
// 1. 通过另外一个对象创建出一个新对象
var o = createObjWithObj(obj);
// 2. 增加对象
o.name = 'sz';
o.age = 18;
// 3. 返回对象
return o;
}
复制代码
六、继承-原型链继承-总结
1. 针对于父类构造函数的实例属性
使用借助构造函数继承的方式,在子构造函数中, 调用父构造函数
- 注意修改this指针
- 注意调用顺序,在子类构造函数内部最前
2.针对于父类构造函数的原型对象属性
- 原型链继承
- 寄生式继承方式
3. 图解
七、结语
到这里我们就结束了继承的知识,明天我们搞一些练习巩固巩固哈,还有一个之前我们说到的拷贝属性,也给他整整。
欢迎前往专栏阅读其他相关的文章:JavaScript版块知识
码字画图不易,如果觉得对你有帮助,觉得还不错的话,欢迎点赞收藏~
当然由于是个人整理,难免会出现纰漏,欢迎留言反馈。