理解js继承的核心(基于原型)

  • 前言:如有不懂可以查看原型/原型链等知识。自es6出来后就有class关键字了,实战不用这么麻烦,但class只是语法糖,核心还是基于原型,理解很有必要

原型链继承

  • 简单理解,串联起各个原型实现继承

  • 通过原型链实现继承时,不能使用对象字面量创建原型方法,否则原型链被重写(原型间的关系断了)

function F1 () {
	this.a = true
}
F1.prototype.method1 = function () {
	return this.a
}

function F2 () {
	this.x = false
}
// 创建F1的实例,使得F2的原型指向了F1的,且F2的原型取得了F1原型中的属性和方法
F2.prototype = new F1() // 继承了F1
F2.prototype.method2 = function () { // F2新增一个方法
	return this.x
}

let instance = new F2()
// instance指向F2的原型,F2的~指向F1的~
console.log(instance.x)        // false
console.log(instance.method1)  // true
  • 弊端(所以实践中很少单独使用该方法)
    • 当牵涉到引用类型值时,所有实例会共享该值,在其中一个实例上改变该值则其他实例调用时也被改变了(因为 ins1 和 ins2 都是引用的 F1 中的a属性)
    • 创建子类型的实例时,不能向超类型的构造函数中传递参数

借用构造函数

  • 在子类构造函数的内部调用超(父)类型构造函数实现继承
function F1 (name) { // 设置参数name
	this.name = name
	this.al = ["a", "b", "c"]
}

function F2 () {
	// 创建实例时,既调用F2的构造函数,又调用F1的,则实例兼具它两的属性、方法
	F1.call(this, "Eric") // 继承,同时子类(F2)向父类(F1)传参
	this.age = 20 // 为了防止父类重写子类属性,子类定义的属性应在继承语句后
}

let ins1 = new F2()
ins1.al.push("d") // 向al数组中添加一个 “d”,仅针对该实例而言
let ins2 = new F2()

console.log()
console.log(ins1.name + ins1.age) // Eric20
console.log(ins1.al) // ["a", "b", "c", "d]
console.log(ins2.al) // ["a", "b", "c"],ins1并未影响ins2
// 因为每个实例创建时都是重新执行F1的构造函数,取得的属性(此处是al)是副本
  • 弊端(虽然该方法解决了原型链继承的两个弊端)
    • 方法都在构造函数中定义,函数无法复用
    • 父类原型定义的方法子类不能用(显然,没有串联起原型)

组合继承

  • 将上述两种方法结合:原型链实现原型上的继承;构造函数实现实例上的继承
  • 实现了函数复用,保证每个实例都有自己的属性
// 基本上就是把之前的代码拼接起来
function F1 (name) {
	this.name = name
	this.al = ["a", "b", "c"]
}
F1.prototype.method1 = function () {
	console.log(this.name)
}
function F2 (name, age) { //继承属性
	F1.call(this, name)
	this.age = age
} //继承方法
F2.prototype = new F1()
// 此处F2的prototype被F1的覆盖,导致F2失去默认的constructor,需要修正回来
// constructor:返回创建实例对象的构造函数的引用。
F2.prototype.constructor = F2
F2.prototype.method2 = function () {
	console.log(this.age)
}
  • 弊端:无论什么情况下,都会调用两次超类型构造函数

原型式继承

  • 借助原型基于已有的对象创建新对象,同时不用创建自定义类型
// es5新增Object.create()方法取代了这个
function Obj (o) {
	function F () {} // 创建一个临时性的构造函数
	F.prototype = o  // o对象作为构造函数的原型
	return new F()   // 返回该临时类型的一个新实例
}

let person = {
	name: "eric",
	arr: ["a", "b"]
}

let x = Obj(person) // es5调用:Object.create(person)
x.name = "mike"
x.arr.push("c")

let y = Obj(person)
y.name = "amy"
y.arr.push("d")

console.log(x.arr) // ["a", "b", "c", "d"]
  • 弊端:用原型继承的通病,引用类型的值共享,任何一个实例的该值改变,其他实例的也会变

寄生式继承

  • 创建一个仅用于封装继承过程的函数,函数内部实现继承,最后返回对象
function F1 (o) {
	let x = Obj(o)      // 上文中的Obj函数
	x.say = function () {  // 给o加方法
		console.log("hi")
	}
	return x               // 返回一个新对象(携带了say方法)
}

let person = {
	name: "eric"
}

let y = F1(person)
y.say() // "hi"
  • 弊端:显然,代码难以复用。想要生成不同的对象就得改写 F1 函数

寄生组合式继承

  • 通过借用构造函数来继承属性,通过原型链的混合形式继承方法
// 寄生组合式继承函数
// 解决组合继承调用两次F3的弊端,同时避免在父类的原型上创建多余的属性
function F1 (F2, F3) { // F2是子类,F3是父类
	// Obj()为前文对应的函数
	let p = Obj(F3.prototype) // 创建F3原型的副本p
	// 给p加上由于重写原型而失去的“默认”的constructor属性
	p.constructor = F2 
	F2.prototype = p // 将p赋值给F2的原型
}

function F3 (name) {
	this.name = name
	this.x = ["a", "b"]
}
F3.prototype.say1 = function () {
	console.log(this.name)
}

function F2 (name, age) {
	F3.call(this, name) // 仅调用一次F3的构造函数
	this.age = age
}

F1(F2, F3) // 实现继承
F2.prototype.say2 = function () {
	console.log(this.age)
}

// 检测
let f1 = new F3("mike")
let f2 = new F2("eric", "12")
console.log(f1.name + " " + f2.name) // eric mike
console.log(f2.age + " " + f2.say2()) // 12 12
console.log(f1.say2) // undefined,说明f1的原型上没有新增say2()函数
  • 终于分析完了!既然你都看到这里了,觉得有用的话不妨给作者点个赞吧~
已标记关键词 清除标记
相关推荐
<p> <strong><span style="font-size:20px;color:#FF0000;">本课程主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者</span></strong> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">1. 包含:<span style="color:#FFFF00;background-color:#FF0000;">项目源码、</span><span style="color:#FFFF00;background-color:#FF0000;">项目文档、数据库脚本、软件工具</span>等所有资料</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">2. 手把手的带你从零开始部署运行本套系统</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">3. 该项目附带的源码资料可作为毕设使用</span></strong></span> </p> <p> <span style="color:#FF0000;"><strong><span style="font-size:18px;">4. 提供技术答疑和远程协助指导</span></strong></span><strong><span style="font-size:18px;"></span></strong> </p> <p> <br /> </p> <p> <span style="font-size:18px;"><strong>项目运行截图:</strong></span> </p> <p> <strong><span style="font-size:18px;">1)系统登陆界面</span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015433522.png" alt="" /><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">2)学生模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241015575966.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">3)教师模块</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016127898.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">4)系统管理员</span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016281177.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><img src="https://img-bss.csdn.net/202002241016369884.png" alt="" /></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p> <p> <strong><span style="font-size:18px;"><strong><span style="font-size:18px;">更多Java毕设项目请关注我的毕设系列课程 <a href="https://edu.csdn.net/lecturer/2104">https://edu.csdn.net/lecturer/2104</a></span></strong></span></strong> </p> <p> <strong><span style="font-size:18px;"><br /> </span></strong> </p>
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页