一、为什么要继承?
1.为了把公共的内容提取出来变成更加公共的内容
2.为了让所有的类都能使用
二、继承的含义?
- 当 A 构造函数的实例, 能够使用 B 构造函数的 属性(构造函数体内)和方法(构造函数原型)
- 我们说 A 构造函数(类) 继承自 构造函数B(类)
+ 我们管 A 构造函数(类) 叫做 子类
+ 我们管 B 构造函数(类) 叫做 父类
三、原型继承?
原型继承
+ 核心: 子类的原型指向父类的实例
=> 核心: 子类的原型指向父类的实例
+ 通过改变原型链来实现继承
=> 当你访问一个对象的成员的时候
=> 会按照原型链规则查找
+ 优点
=> 属性和方法 都能继承下来
+ 缺点
=> 子类不能存在自己的私有方法, 一旦你想 子类.prototype 上插入方法, 就是在向父类的实例身上插入
=> 继承下来的属性是一模一样的
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender) {
this.gender = gender
}
// 实现继承
Student.prototype = new Person('Rose', 18)
const s = new Student('男')
console.log(s)
console.log(s.gender)
console.log(s.name)
s.sayHi()
const s2 = new Student('女')
console.log(s2)
四、借用继承
借用继承 / call 继承(需要利用 call 或者 apply 方法)
+ 核心: 利用 call 来调用父类构造函数, 改变构造函数内的 this 指向
=> 父类.call(子类的this)
+ 构造函数也是函数, 通过 call 方法改变 this 指向
=> 实现讲父类内添加的内容插入到子类内
+ 优点
=> 子类可以有自己的原型
=> 每一个继承下来的属性都是自己的独立私有属性
+ 缺点
=> 父类的方法不能继承
// 分析父类:
// 如果你想用 Person 创建实例
// new 关键字做了几个事情
// 创建了一个对象
// this 指向这个对象
// this.__proto__ 指向构造函数的原型
// 返回这个对象
// const p = new Person('Jack', 18)
// 如果没有 new 关键字
// 构造函数的本质, 依旧是函数, 可以不和 new 关键字连用
// 不会自动创建对象了, 不会设置 __proto__ 了, 不会返回这个对象
// 问题: 代码要不要执行 ?
// 单纯的考虑一个函数的调用, this 指向谁了 ?
// 因为 this 指向 window 了
// 所以两个成员添加到 window 身上了
// const p2 = Person('Rose', 20)
// console.log(p2)
// 只要是函数, 就可以通过 call 和 apply 和 bind 方法改变 this 指向
// Person 函数也利用 call 方法改变一下 this
// const obj = {}
// 让 Person 内的 this 指向 obj
// 当 Person 函数执行的时候, 两个成员添加到了 obj 身上
// Person.call(obj, 'Jerry', 22)
// console.log(obj)
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender, name, age) {
// 在这里把 Person 当做普通函数执行一遍
// 并且在调用的时候, 使用 call 来调用, 改变 this 指向
// 在这个函数内, this 指向 Student 的实例
// 我现在利用 call 把 Person 函数内的 this 指向了 Student 的实例
// Person 函数内添加的两个成员, 就加在了 Student 的实例 身上
Person.call(this, name, age)
this.gender = gender
}
// 子类自己的原型方法
Student.prototype.study = function () { console.log('学习中...') }
const s = new Student('男', 'Jack', 18)
const s2 = new Student('女', 'Rose', 20)
console.log(s)
console.log(s2)
五、组合继承1 (原型继承 和 借用继承)
核心: 把 原型继承 和 借用继承 放在一起使用
1.优点:
=> 属性和方法 都能继承下来
=> 每一个继承下来的属性都是自己的独立私有属性
2. 缺点:
=> 没有自己的原型
=> 多一套属性, 虽然没有值, 但是多出来了
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender, name, age) {
// 利用借用继承继承属性
Person.call(this, name, age)
this.gender = gender
}
// 利用原型继承继承方法(属性和方法都能继承)
Student.prototype = new Person()
const s = new Student('男', 'Jack', 18)
console.log(s)
s.sayHi()
六、拷贝继承
1.in 关键字的作用
=> 语法: ‘key’ in 对象
=> 得到结果: 一个布尔值
-> true, 表示该 key 在该对象的原型链上(可能是自己的属性, 也可能是原型上的属性)
-> false, 表示该 key 在该对象的整个原型链上查找不到
-
hasOwnProperty()
=> 语法: 对象.hasOwnProperty(‘key’)
=> 返回值: 一个布尔值
-> true, 表示该 key 是该对象自己本身的属性
-> false, 表示该 key 不是该对象自己本身的属性 -
拷贝继承 / for in 继承
+ 核心: 利用 in 关键字可以遍历到自己和原型上的成员
+ 把父类实例的所有内容, 遍历一份到子类的原型上
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender) {
this.gender = gender
}
Student.prototype.study = function () { console.log('学习中...') }
// 准备一个子类的实例
const p = new Person('Jack', 18)
// 遍历父类的实例
for (let k in p) {
Student.prototype[k] = p[k]
}
const s = new Student('男')
console.log(s)
七、组合继承2 (借用继承 和 拷贝继承)
结合了 借用继承 和 拷贝继承的一部分 实现的继承方案
1.出发点
=> 没有自己的私有属性, 利用 借用继承
=> for in 的遍历和属性不可改, 在书写构造函数的时候, 原型上写的是方法, 构造函数体内写的是属性
-> 如果我的 for in 不去继承属性
-> 之继承原型上的方法
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender, name, age) {
// 利用借用继承继承属性
Person.call(this, name, age)
this.gender = gender
}
Student.prototype.study = function () { console.log('学习中...') }
// 子类的原型继承父类的原型
// 利用 for in 循环只从 父类.prototype 开始遍历
for (let k in Person.prototype) {
Student.prototype[k] = Person.prototype[k]
}
const s = new Student('男', 'Jack', 18)
console.log(s)
八、寄生继承
// 寄生式组合继承(完善)
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
function Student(gender, name, age) {
// 利用借用继承继承属性
Person.call(this, name, age)
this.gender = gender
}
// -----------------------------------------------
; (function () {
// 把 寄生继承 和 借用继承 完美结合
function Third() { }
// 2. 利用赋值操作, 让 Third 的 原型 等于 父类的原型
Third.prototype = Person.prototype
// 3. 利用原型继承, 让 Student 继承 Third
Student.prototype = new Third()
// 因为没有constructor所以自己补了一个
Student.prototype.constructor = Student
})()
// -----------------------------------------------
Student.prototype.study = function () { console.log('学习中...') }
const s = new Student('男', 'Jack', 18)
console.log(s)
九、ES6 的类继承
1.需要用到 extends 关键字
=> 语法: class 子类 extends 父类 {}
2.需要用到 super 关键字(超类)
=> 需要在子类的 构造器(constructor) 内调用使用
=> 注意: 需要调用在 this.xxx 的前面
// 父类
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () { console.log('hello world') }
// 子类
// 创建一个继承自 Person 的 Student 类
class Student extends Person {
constructor (gender, name, age) {
// super 等价于原先的 借用继承
super(name, age)
this.gender = gender
}
study () { console.log('学习中...') }
}
const s = new Student('男', 'Jack', 18)
console.log(s)