● 在 JavaScript 内, 继承是面向对象变成中一个重要的开发思维, 外围就是让 A 对象应用 B 对象的属性和办法. 说白了, 就是为了资源整合, 让咱们的代码复用性更高, 前期的可维护性和可扩展性也更高.
● 在 JS 中, 每一个对象都有一个本人的原型对象, JS 内的继承就是依赖这个原型对象, 而且原型对象也有本人的原型对象, 就是这一个一个的原型对象组成了原型链, 这就是继承的外围.
● 举个例子 :
○ 有一位老父亲, 致力了半辈子, 盖了一个大房子
○ 这个时候, 不论这个老父亲有多少个儿子, 他的儿子都是能够住在这个房子外面的
○ 能够用这个房子所有内容
○ 然而如果要是没有继承的话, 那么他的每一个儿子都要本人去从新盖房子
● 这就是咱们继承的意义, 接下来咱们就具体的说一说 JavaScript 内常见的继承计划
回顾之前的知识点
原型
● 构造函数(类) 有一个本人的属性 prototype, 是一个对象
● 每一个对象都有一个本人的属性 __proto__, 指向所属构造函数的 prototype
● 这样, 就将构造函数和对象连接了起来
原型链
● 原型链就是 proto 串联起来的对象链状构造
● 每一个对象都会有本人的原型对象(proto )
原型链拜访规定
● 对象成员的拜访规定就是依照原型链的机制进行拜访的
● 当你拜访一个对象的成员的时候
○ 会首先在对象本人内查找
○ 如果本人有, 间接应用本人的
○ 如果本人没有, 就会主动去到本人的 proto 查找
○ 如果还没有, 在持续去 proto 查找
○ 以此类推
○ 直到 Object.prototype 都没有, 那么返回 null
常见继承计划
- 原型链继承
● 实质就是在原型链上做一些手脚, 让某一个对象的 proto 能指向另一个对象就好了
● 外围 : 子类的原型指向父类的实例
// 父类
function Person(name) {this.name = name}
Person.prototype.sayHi = function () {console.log('hello world')
}
// 子类
function Student(age) {this.age = age}
// 实现继承
Student.prototype = new Person('千锋大前端')
const s = new Student(10)
console.log(s)
● 毛病 :
○ 子类没有本人的原型, 因为子类的原型就是父类的实例
○ 在继承属性这方面, 所有子类实例对象继承下来的属性都是截然不同的
● 长处 :
○ 属性和办法都能继承下来
- 借用构造函数继承
● 实质就是把父类构造函数当做一个一般函数来应用, 在利用一些扭转 this 指向的办法
● 外围 : 利用 call 或者 apply 办法调用父类构造函数, 让其 this 指向子类的实例
// 父类
function Person(name) {this.name = name}
Person.prototype.sayHi = function () {console.log('hello world')
}
// 子类
function Student(age, name) {
this.age = age
// 实现继承
Person.call(this, name)
}
const s = new Student(10, '千锋大前端')
console.log(s)
● 毛病 :
○ 只能继承父类的属性, 原型上的办法不能继承
● 长处 :
○ 子类有本人的原型, 能够向本人的原型上增加专属于子类的内容
○ 对于继承的属性而言, 每一个子类的实例都会继承一套本人的属性
- 根底组合继承
● 实质就是把下面的 原型继承 和 借用构造函数继承 组合在一起应用而已
● 将以上两种继承形式的长处全副合并在一起, 号称最早的完满继承
// 父类
function Person(name) {this.name = name}
Person.prototype.sayHi = function () {console.log('hello world')
}
// 子类
function Student(age) {
this.age = age
// 实现继承(目标 : 继承属性)
Person.call(this, name)
}
// 实现继承(目标 : 继承办法, 属性不重要)
Student.prototype = new Person()
const s = new Student(10, '千锋大前端')
console.log(s)
- 寄生继承
● 实质就是是利用一个空对象来包装父类的实例对象,而后再将该空对象的原型设置为父类的原型对象,最初将该空对象返回作为子类的实例对象,从而实现继承。
function Parent(name) {this.name = name;}
Parent.prototype.sayHi = function() {console.log('hello world')
}
function Student(parent, name) {let student = Object.create(parent)
student.name = name
student.sayHello = function() {console.log(`Hello, my name is ${this.name}`)
}
return student
}
let parent = new Parent('千锋大前端')
let student = createChild(parent, '千锋大前端')
student.sayHello()
student.sayHi()
- 寄生组合继承
● 这是一个十分经典的继承计划, 他是联合了 寄生继承 和 根底组合继承 两者发明进去的. 并且解决了组合继承中反复调用构造函数的难堪, 同时也很好的防止了寄生继承中的原型链净化, 是我集体感觉十分优良得以中继承计划, 不晓得是哪一个大神想进去的计划.
● 在寄生组合继承中,咱们首先应用组合继承的形式创建对象和原型之间的关系,而后通过寄生继承的形式修改原型上不须要的属性
// 寄生继承实现函数
function inheritPrototype(student, person) {
// 创立父类原型的一个正本
let prototype = Object.create(person.prototype)
// 修改正本的 constructor 属性
prototype.constructor = student
// 将修改后的原型赋值给子类
student.prototype = prototype
}
// 父类
function Person(name) {this.name = name}
Person.prototype.sayHi = function() {console.log('hello world')
}
// 子类
function Student(age, name) {
this.age = age
// 借用继承(目标 : 继承属性)
Person.call(this, name)
}
// 寄生继承
inheritPrototype(Student, Person)
// 向子类本人的原型上增加办法
Student.prototype.sayHello = function() {console.log(`Hello, My name is ${ this.name}`);
}
let instance = new Student(10, '千锋大前端')
instance.sayHello()
instance.sayHi()
- ES6 的类继承语法
● 在 ES6 标准中, 呈现了类继承的语法
● 也就是说, 咱们能够不须要以上这些乌七八糟的玩意了, 间接上语法
● 咱们能够间接应用 extends 关键字进行父类的继承
○ 留神 : 还须要在类的结构器内写一个 super 来继承父类属性
// 父类
class Person {constructor(name) {this.name = name;}
sayHi () {console.log('Hello world')
}
}
// 子类, 间接应用 extends 关键字继承父类
class Student extends Person {constructor(name, age) {
// 继承父类属性
super(name)
this.age = age
}
sayAge() {console.log(`Hello, My name is ${ this.name}`)
}
}
let instance = new Student('千锋大前端', 10)
instance.sayHello()
instance.sayHi()