javascript面向对象之继承(上)

47次阅读

共计 2575 个字符,预计需要花费 7 分钟才能阅读完成。

我们之前介绍了 javascript 面向对象的封装的相关内容,还介绍了 js 的 call 方法,今天开始讨论 js 的继承这篇文章参考了《javascript 高级程序设计》(第三版),但内容不局限于,网上很多关于 js 继承的相关内容都是来自于这本书,有兴趣的同学可以翻阅查看
原型链继承
我们先通过一个栗子,了解一下原型链继承。留意代码注释内容
// 创建自定义构造函数
function Hqg() {
this.name = ‘ 洪七公 ’;
}
// 在当前构造函数的原型链上添加属性 skill
Hqg.prototype.skill = ‘ 打狗棒 ’

// 通过自定义构造函数 Hqg 实例化一个对象 gj
const gj = new Hqg()
console.log(gj.skill);//=> 打狗棒
// 通过自定义构造函数 Hqg 实例化一个对象 hr
const hr = new Hqg()
console.log(hr.skill);//=> 打狗棒
一个简单的栗子,郭靖和黄蓉都从洪七公那里继承到了 skill 打狗棒,貌似没什么问题,我们继续看 demo
// 上面代码省略
const gj = new Hqg()// 通过自定义构造函数 Hqg 实例化一个对象 gj
gj.skill = “ 降龙十八掌 ”// 重新对 gj 的 skill 赋值
console.log(gj.skill);//=> 降龙十八掌
// 通过自定义构造函数 Hqg 实例化一个对象 hr
const hr = new Hqg()
console.log(hr.skill);//=> 打狗棒
对郭靖的 skill 重新赋值,也没有影响黄蓉的 skill,我们打印一下 gjgj 的 skill 屏蔽掉了原型链上的 skill,所以 gj 的 skill 是降龙十八掌,而 hr 的 skill 依然是打狗棒
问题即将暴露
function Hqg() {
this.name = ‘ 洪七公 ’;
}
Hqg.prototype.skill = [‘ 打狗棒 ’]

const gj = new Hqg()
gj.skill.push (“ 降龙十八掌 ”)// 找到了原型链中的 skill,并对其执行 push 操作,从而改变了构造函数中的 skill 属性
console.log(gj.skill); //=>[“ 打狗棒 ”, “ 降龙十八掌 ”]
const hr = new Hqg()
// 构造函数中的 skill 已经被改变
console.log(hr.skill); //=>[“ 打狗棒 ”, “ 降龙十八掌 ”]
总结一下,gj 和 hr 都是 Hqg 的实例,继承 Hqg 的属性和方法,当 Hqg 的属性或者方被改变了,后面的实例也会受影响,有时候这并不是我们希望的结果
借用构造函数
借用?就是使用 call 或者 apply 改变一下 this 指向,就是子类的构造函数内部通过 call 或者 apply 调用父类的构造函数,如果对 call 方法有不了解的地方,可以翻看昨天的文章 举一个栗子
// 创建一个构造函数,并添加一些属性
function Hqg() {
this.name = ‘ 洪七公 ’;
this.job = ‘ 帮主 ’;
this.skill = [‘ 降龙十八掌 ’, ‘ 打狗棒 ’]
}
// 创建一个构造函数,并借用了 Hqg 的构造函数
function Hr() {
Hqg.call(this)
this.name = ‘ 黄蓉 ’;
this.job = [‘ 相夫 ’, ‘ 教子 ’]
}
// 创建一个构造函数,并借用了 Hqg 的构造函数
function Gj() {
Hqg.call(this)
this.name = ‘ 郭靖 ’;
this.job = [‘ 吃饭 ’, ‘ 睡觉 ’]

}
const hr = new Hr();
console.log(hr);

const gj = new Gj();
console.log(gj);
输出这样就避免了原型链继承中,构造函数中的属性或者方法被其他实例所改变的问题⚠️:这里要注意 call 方法的执行顺序:
// 部分代码省略
function Hr() {
this.name = ‘ 黄蓉 ’;
this.job = [‘ 相夫 ’, ‘ 教子 ’]
Hqg.call(this)
}
function Gj() {
this.name = ‘ 郭靖 ’;
this.job = [‘ 吃饭 ’, ‘ 睡觉 ’]
Hqg.call(this)
}
// 部分代码省略
如果 call 在之后执行就会导致一个问题值会被覆盖,这个要注意!
借用构造函数进行传参
这个算是一个升级的玩法吧
function Hqg(name,job,skill) {
this.name = name;
this.job = job;
this.skill = skill
}
function Hr() {
Hqg.call(this,’ 黄蓉 ’,[‘ 相夫 ’, ‘ 教子 ’],[‘ 打狗棒 ’])
}

function Gj() {
Hqg.call(this,’ 郭靖 ’,[‘ 吃饭 ’, ‘ 睡觉 ’],[‘ 降龙十八掌 ’])
}
const hr = new Hr();
console.log(hr);

const gj = new Gj();
console.log(gj);
输出
组合继承
将原型链和借用构造函数技术组合到一起。使用原型链实现对原型属性和方法的继承,用借用构造函数模式实现对实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有自己的属性一个栗子
function Hqg(name) {
this.name = name
this.skill = [“ 降龙十八掌 ”,” 打狗棒 ”]
}
Hqg.prototype.sayName = function () {
console.log(this.name);
}
function Hero(name, job) {
Hqg.call(this, name);
this.job = job
}
Hero.prototype = new Hqg();
Hero.prototype.constructor = Hero;
Hero.prototype.sayJob = function () {
console.log(this.job)
}
var gj = new Hero(‘ 郭靖 ’, ‘ 吃饭睡觉 ’);
gj.skill.push(“ 九阴真经 ”);
console.log(gj);
var hr = new Hero(‘ 黄蓉 ’, ‘ 相夫教子 ’);
console.log(hr);
先看下输出我们把这个组合继承和之前的两个原型链继承和借用构造函数继承进行比较不难发现组合继承融合了他们的优点,成为 javascript 中最常用的继承模式今天就讨论前三个,还有三个明天继续,不见不散

参考链接你们真的了解 JS 的继承嘛?

正文完
 0