繼承
●要知道什么是繼承
●要知道繼承的方式有哪些
●每種的繼承方式是如何實(shí)現(xiàn)的
什么是繼承
●繼承關(guān)系出現(xiàn)在構(gòu)造函數(shù)和構(gòu)造函數(shù)之間
●當(dāng)構(gòu)造函數(shù)A 的實(shí)例使用了 構(gòu)造函數(shù)B 的屬性和方法
●我們就說(shuō) 構(gòu)造函數(shù)A 繼承自 構(gòu)造函數(shù)B
○管 構(gòu)造函數(shù)A 叫做子類
○管 構(gòu)造函數(shù)B 叫做父類
●總結(jié) :繼承就是獲取存在對(duì)象已有屬性和方法的一種方式
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function () { console.log('玩游戲') }
// 將來(lái)我創(chuàng)建的 Person 的實(shí)例, 就會(huì)出現(xiàn)兩個(gè)屬性一個(gè)方法
const p = new Person('Jack', 18)
console.log(p)
function Student(classRoom) {
this.classRoom = classRoom
}
// 將來(lái)我創(chuàng)建的 Student 的實(shí)例
// 當(dāng)我的 s 實(shí)例能使用 name 屬性, age 屬性 和 play 方法的時(shí)候
// 我們就說(shuō) Student 構(gòu)造函數(shù)繼承自 Person 構(gòu)造函數(shù)
// Student 就是 子類
// Person 就是 父類
const s = new Student(2114)
console.log(s)
繼承的方式有哪些
原型繼承
●核心: 讓子類的原型指向父類的實(shí)例
●優(yōu)點(diǎn):
○父類構(gòu)造函數(shù)體內(nèi)的屬性和原型上的方法都可以實(shí)現(xiàn)繼承
●缺點(diǎn):
○繼承下來(lái)的屬性不在自己身上, 在自己的原型上
○一個(gè)構(gòu)造函數(shù)的實(shí)例, 需要在兩個(gè)地方傳遞參數(shù)
○所有子類的實(shí)例, name 和 age 一模一樣
<script>
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戲')
}
// 子類
function Student(classRoom) {
this.classRoom = classRoom
}
// 實(shí)現(xiàn)繼承
const p = new Person('Jack', 18)
Student.prototype = p
// 此時(shí) s 的 __proto__ 指向誰(shuí) ?
// 指向所屬構(gòu)造函數(shù)的 prototype
// 因?yàn)?Student.prototype 就是 就是 Person 的實(shí)例
// s 的 __proto__ 就是 Person 的實(shí)例
const s = new Student(2114)
console.log(s)
// 當(dāng)你訪問(wèn) s 的 classRoom 成員的時(shí)候
// 自己有直接使用
console.log(s.classRoom)
// 當(dāng)你訪問(wèn) s 的 name 成員的時(shí)候
// 自己沒有, 去到自己的 __proto__ 上查找
// 因?yàn)樽约旱腳_proto__ 就是 Person 的實(shí)例
// 其實(shí)就是去到 Person 的實(shí)例上查找
console.log(s.name)
// 當(dāng)你訪問(wèn) s 的 play 成員的時(shí)候
// 自己沒有, 去到自己的 __proto__ 上查找
// 也就是去到 Person 的實(shí)例上查找, 發(fā)現(xiàn)還是沒有
// 就再去 __proto__ 上查找
// 自己的 __proto__ 的 __proto__
// Person 實(shí)例 的 __proto__
// Person 實(shí)例 的 __proto__ 就是 Person.prototype
s.play()
const s2 = new Student(2115)
console.log(s2)
</script>
借用構(gòu)造函數(shù)繼承
●核心: 把父類構(gòu)造函數(shù)當(dāng)做普通函數(shù)調(diào)用, 并且改變其 this 指向
●優(yōu)點(diǎn):
○子類的所有繼承下來(lái)的屬性都在自己身上
○子類的所有參數(shù)在一個(gè)地方傳遞
○子類的所有實(shí)例都可以給繼承下來(lái)的屬性賦不一樣的值
●缺點(diǎn):
○父類的原型上的方法沒有繼承下來(lái)
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戲')
}
// 子類
function Student(classRoom, name, age) {
this.classRoom = classRoom
// 實(shí)現(xiàn)繼承
Person.call(this, name, age)
}
const s = new Student(2114, 'Jack', 18)
console.log(s)
const s2 = new Student(2115, 'Rose', 20)
console.log(s2)
組合繼承
●核心: 把原型繼承和借用構(gòu)造函數(shù)繼承放在一起使用
●優(yōu)點(diǎn):
○都能繼承下來(lái)
○屬性在自己身上, 每一個(gè)子類實(shí)例繼承的屬性值都可以不一樣
●缺點(diǎn):
○子類的原型上多了一套屬性
<script>
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.play = function() {
console.log('玩游戲')
}
// 子類
function Student(classRoom, name, age) {
this.classRoom = classRoom
// 借用繼承
// 目的: 把屬性繼承在自己身上
Person.call(this, name, age)
}
// 原型繼承
// 目的: 繼承父類原型上的方法
Student.prototype = new Person()
// 創(chuàng)建子類的實(shí)例
const s = new Student(2114, 'Rose', 20)
console.log(s)
</script>
ES6類繼承
●類的繼承是ES6中提出的一種繼承方式
●這個(gè)繼承有了語(yǔ)法的規(guī)定,必須要按照這樣的方式來(lái)繼承
●類的繼承的實(shí)現(xiàn): 兩個(gè)步驟實(shí)現(xiàn)繼承
○書寫子類的時(shí)候, 加上 extends 關(guān)鍵字
■class 子類 extends 父類 {}
■目的: 繼承父類原型上的方法
○在子類的 constructor 內(nèi)書寫 super()
■super(實(shí)參)
■目的: 繼承父類的屬性
●注意:
○必須要書寫 super 和 extends
○在子類的 constructor 內(nèi) super 必須寫在 this.xxx 的前面(最前面)
●父類可以是構(gòu)造函數(shù),但是子類不能的構(gòu)造函數(shù)因?yàn)閑xtends和super關(guān)鍵字就是給類設(shè)計(jì)的
<script>
// 父類
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
play() {
console.log('玩游戲')
}
}
// 父類
// function Person(name, age) {
// this.name = name
// this.age = age
// }
// Person.prototype.play = function () { console.log('玩游戲') }
// 子類
class Student extends Person {
constructor(classRoom, name, age) {
super(name, age)
this.classRoom = classRoom
}
study() {
console.log('學(xué)習(xí)')
}
}
const s = new Student(2114, 'Jack', 18)
console.log(s)
class Teacher extends Person {
constructor(gender, name, age) {
super(name, age)
this.gender = gender
}
}
const t = new Teacher('男', 'Jack', 20)
console.log(t)
</script>
拷貝繼承
●利用 for in 循環(huán)遍歷對(duì)象
●把所有的內(nèi)容復(fù)制一份放在子類的原型上
// 書寫 for in 循環(huán)的時(shí)候, 不光可以遍歷到對(duì)象自己身上的屬性, 也可以遍歷到原型上的屬性
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 創(chuàng)建一個(gè)父類的實(shí)例
// const p = new Person('Jack', 18)
// console.log(p)
// for (let k in p) {
// console.log(k)
// }
// 子類
function Student(gender, ...arg) {
this.gender = gender
// 創(chuàng)建一個(gè)父類的實(shí)例
const p = new Person(...arg)
// 利用 for in 循環(huán)繼承
for (let k in p) {
// 隨著循環(huán), k 分別是 name age 和 sayHi
Student.prototype[k] = p[k]
}
}
const s = new Student('男', 'Jack', 18)
console.log(s)
console.log(s.name)
const s2 = new Student('女', 'Rose', 20)
console.log(s2)
console.log(s2.name)
寄生式繼承
/*
寄生式繼承1
*/
// 父類
function Person(name) {
this.name = name
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子類
// 構(gòu)造函數(shù)內(nèi)不要寫 return
function Student() {
// 直接在子類里面 return 一個(gè)父類的實(shí)例
const p = new Person('Jack')
return p
}
// 創(chuàng)建一個(gè)子類的實(shí)例
// 看似得到的是 Student 的實(shí)例, 但是其實(shí)得到的還是 Person 的實(shí)例
const s = new Student()
console.log(s)
/*
寄生式繼承2 - 對(duì)寄生式繼承1 的 改造
*/
// 父類
function Person(name) {
this.name = name
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子類
// 構(gòu)造函數(shù)內(nèi)不要寫 return
function Student(gender) {
this.gender = gender
}
// 寄生一下 父類的原型
// Student的原型指向 Person的原型
Student.prototype = Person.prototype
// 創(chuàng)建一個(gè)子類的實(shí)例
// Student 自己沒有原型使用了, 直接使用 Person 的原型
const s = new Student('男')
console.log(s)
// 寄生式組合繼承
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 實(shí)現(xiàn)繼承
// 借助一個(gè)第三方構(gòu)造函數(shù)
function Third() {}
// 第三方構(gòu)造函數(shù)去繼承 父類
// 利用寄生繼承的方式來(lái)實(shí)現(xiàn)
// 第三方的 原型 指向 父類的原型
Third.prototype = Person.prototype
// 將來(lái)我使用第三方創(chuàng)建實(shí)例的時(shí)候
const t = new Third()
// 子類
function Student(...arg) {
// 利用 call 繼承
Person.call(this, ...arg)
}
// 子類去想辦法繼承第三方的內(nèi)容
// 利用原型繼承去繼承第三方內(nèi)容
// 子類的原型指向第三方的實(shí)例
Student.prototype = new Third()
const s = new Student('Jack', 18)
console.log(s)
// 利用了一個(gè)自執(zhí)行函數(shù)
// 自執(zhí)行函數(shù), 不需要名字的函數(shù)
;(function () {
var a = 100
console.log('你好 世界')
})()
// 子類
function Student(gender, ...arg) {
this.gender = gender
Person.call(this, ...arg)
}
// 把 第三方內(nèi)容 放在自執(zhí)行函數(shù)內(nèi)
(function () {
function Third() {}
Third.prototype = Person.prototype
Student.prototype = new Third()
})()
const s = new Student('男', 'Jack', 18)
console.log(s)
冒充式繼承
/*
冒充繼承
+ 利用了一個(gè)淺拷貝 + 寄生繼承
*/
// 父類
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
const p = new Person('Jack', 18)
// 寄生繼承
function Student(gender) {
this.gender = gender
// 創(chuàng)建完畢實(shí)例以后, 拷貝一個(gè)父類的實(shí)例
Object.assign(this, p)
}
Student.prototype = Person.prototype
const s = new Student('男')
console.log(s)