javascript中的继承
本章节摘自《javascript设计模式》人民邮电出版社中的继承一节,感觉收获很大,便自己作笔记整理一下,方便更好的理解
首先,继承是什么
继承? 什么是继承呢,顾名思义,比如父母会把自己的一些特点遗传给孩子,孩子具有了父母的一些特点,但又不完全一样,总会有自己的特点, 但又不完全一样,总会有自己的特点,所以父母和孩子都是独立的个体,继承可以使得子类具有父类的属性和方法。 但是javascript中并不存在现有的机制,怎么使用javascript实现继承呢
javascript实现继承该如何实现呢
- 第一种: 子类的原型对象–类式继承
声明父类, 当我们实例化一个父类的时候,新创建的对象复制了父类的构造函数内的属性和方法,并将_proto_指向了父类的原型对象, 这样既拥有了父类自己的属性和方法,也拥有了父类原型的属性与方法
(function () {
// subclass prototype --- > superClass 实例 ---> 访问superClass 属性与方法
// subclass prototype._proto_ = superClass.prototype
function SuperClass(id) {
this.books = ['javascript', 'html', 'css', 'vue', 'react']
this.SuperValue = true
this.id = id
}
// 添加共有方法
SuperClass.prototype.getSuperValue = function () {
return this.SuperValue
}
function subClass(id) {
this.SubValue = false
this.subid = id
}
// 继承父类, 将子类原型继承于创建出来的父类的实例
subClass.prototype = new SuperClass(this.subid)
subClass.prototype.getSubValue = function () {
return this.SubValue
}
var instance = new subClass(1)
var instance1 = new subClass(2)
console.log(instance.SuperValue)
console.log(instance.SubValue)
// 缺点: instanceOf只可以检测当前实例是否在某个类的原型链上, 无法判断继承,继承并非实例, 这一点不能弄混
console.log(instance instanceof subClass) // true
console.log(instance instanceof SuperClass) // true
// 通过superclass 的实例 赋值给subclass.prototype
console.log(subClass.prototype instanceof SuperClass) // true
console.log(instance instanceof Object) // 创建的所有对象都是Object的实例 true
console.log(subClass instanceof SuperClass) // false
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
console.log(instance)
instance.books.push('设计模式')
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react", "设计模式"]
类式继承的缺点
1: 由于子类通过其原型prototype对父类进行的实例化,继承了父类,如果父类中的共有属性为引用类型,如果子类修改了父类的属性, 那么,就会直接影响到其他子类
2: 由于子类实现的继承是靠其原型prototype 对父类进行实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因而在创建父类的时候,也无法对父类构造函数的属性进行初始化
- 构造函数继承
(function () {
// superClass this指向 superClass
function superClass(id) {
this.books = ['javascript', 'html', 'css', 'vue', 'react'],
this.id = id
}
superClass.prototype.showBooks = function () {
console.log(this.books)
}
// subClass this指向 superClass
function subClass(id) {
// call方法可以改变函数的作用环境, 因此,在子类中,就是将子类的变量在父类中执行一遍
superClass.call(this, id)
}
var instance1 = new subClass(10)
var instance2 = new subClass(11)
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
instance2.books.push('设计模式')
console.log(instance2.books) // ["javascript", "html", "css", "vue", "react", "设计模式"]
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
console.log(instance2.id) // 11
console.log(instance1.id) // 10
console.log(instance1)
// 缺点: 只是通过改变this指向,拿到父类的属性和方法,并没有继承父类的prototype
// instance1.showBooks() // instance1.showBooks is not a function
})()
构造函数继承的缺点: 是通过改变this指向,拿到父类的属性和方法,并没有继承父类的prototype,如果你想直接使用父类构造函数原型上的方法就会报错,如instance1.showBooks is not a function
- 第三种继承,组合继承(集合前两种继承的优点,互补)
(function () {
function superClass(name) {
this.books = ['javascript', 'html', 'css', 'vue', 'react'],
this.name = name
}
superClass.prototype.showBooks = function () {
console.log(this.books)
}
function subClass(name, time) {
// call方法可以改变函数的作用环境, 因此,在子类中,就是将子类的变量在父类中执行一遍
superClass.call(this, name)
this.time = time
}
subClass.prototype = new superClass()
subClass.prototype.getTime = function() {
console.log(this.time)
}
var instance1 = new subClass('js book', 2014)
// var instance2 = new subClass('js book', 2014)
instance1.books.push('设计模式')
console.log(instance1.books)
// console.log(instance2.books)
// instance2.showBooks()
})()
组合继承的缺点,父类构造函数会被执行两次,在执行call方法时,父类构造函数会执行一次, 在将父类实例赋值给子类原型时,会再次执行一次
- 第四种继承方案: 原型式继承
概念: 声明一个过渡函数对象, 相当于类式继承中的子类,它是对类式继承的封装,只是对类式继承的增强,但是并没有解决类式继承带来的副作用
(function(){
function inheritObject(o) {
// 声明一个过渡函数对象, 相当于类式继承中的子类
// 相对于类式继承的优点,由于F过渡类中的构造函数中没有内容,开销比较小,使用起来也比较方便,但是并没有解决
// 类式继承的缺点
function F() {}
F.prototype = o
// 返回过度对象中的实例,该实例的原型继承了父对象
return new F()
}
var book = {
books: ['javascript', 'html', 'css', 'vue', 'react'],
name: 'book1'
}
var newBook = inheritObject(book)
newBook.name = 'book'
newBook.books.push('设计模式')
console.log(newBook.books) // ["javascript", "html", "css", "vue", "react", "设计模式"]
var newBook1 = inheritObject(book)
newBook1.name = 'book1'
newBook1.books.push('算法')
console.log(newBook.name) // book
console.log(newBook1.name) // book1
console.log(newBook1.books)// ["javascript", "html", "css", "vue", "react", "设计模式", "算法"]
})()
- 第五种继承方式, 寄生式继承
寄生式继承, 顾名思义,就像寄生虫一样寄托于某个对象内部生长, 是指依托于原型继承, 并且可以创建自己的属性和方法
(function(){
function inheritObject(o) {
function F() {}
F.prototype = o
// 返回过度对象中的实例,该实例的原型继承了父对象
return new F()
}
var book = {
books: ['javascript', 'html', 'css', 'vue', 'react'],
name: 'book1'
}
function createBook(obj) {
var o = inheritObject(obj)
// 基于父对象拓展出的新对象
o.getName = function() {
return this.name
}
return o
}
var newBook = createBook(book)
newBook.name = 'book'
newBook.books.push('设计模式')
console.log(newBook.books) // ["javascript", "html", "css", "vue", "react", "设计模式"]
var newBook1 = createBook(book)
newBook1.name = 'book1'
newBook1.books.push('算法')
console.log(newBook.getName())
console.log(newBook1.books)// ["javascript", "html", "css", "vue", "react", "设计模式", "算法"]
})()
- 第六种继承方式, 寄生组合继承(终极继承方式)
(function () {
function inheritObject(o) {
function F() {}
F.prototype = o
// 返回过度对象中的实例,该实例的原型继承了父对象
return new F()
}
function inheritPrototype(subClass, superClass) {
// 得到父对象的副本
var p = inheritObject(superClass.prototype)
p.constructor = subClass
// 给子类的原型重新赋值为父对象的副本
subClass.prototype = p
// 通常副本通过原型继承就可以得到,这里为什么不直接赋值呢,因为这么赋值对父类原型对象复制得到的复制对象
// p中的constructor指向的不是subClass的子类对象,因此在此继承中要对复制对象p做一次增强,修复其constructor指向不正确的问题
// 最后将得到的复制对象p赋值给子类的原型
}
function superClass(name) {
this.name = name
this.books = ['javascript', 'html', 'css', 'vue', 'react']
}
superClass.prototype.getName = function () {
console.log(this.name)
}
function subClass(name, time) {
superClass.call(this, name)
this.time = time
}
inheritPrototype(subClass, superClass)
subClass.prototype.getTime = function () {
console.log(this.time)
}
var newBook1 = new subClass('book', 2014)
var newBook2 = new subClass('book1', 2015)
newBook1.books.push('设计模式')
console.log(newBook1.books) // ["javascript", "html", "css", "vue", "react", "设计模式"]
console.log(newBook2.books) // ["javascript", "html", "css", "vue", "react"]
newBook1.getTime() // 2014
})()
- 第七种继承方式,es6的class继承
class 可以理解为function,class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,也时基于prototype原型链查找,当调用super时,相当于自动执行了call方法,更改子类的this指向,通过调用Object.create(),将子类_proto_指向父类
class superClass {
//构造函数,里面写上对象的属性
constructor(props) {
this.books = ['javascript', 'html', 'css', 'vue', 'react']
}
//方法写在后面
getBooks = function () {
return this.books
}
}
//class继承
class subClass extends superClass {
//构造函数
constructor(props) {
//props是继承过来的属性
//调用实现父类的构造函数
super(props) //相当于获得父类的this指向
}
addBooks(book) {
this.books.push(book)
}
getBooks() {
return this.books
}
}
var newBook1 = new subClass()
var newBook2 = new subClass()
newBook1.addBooks('设计模式')
console.log(newBook1.getBooks()) // (6) ["javascript", "html", "css", "vue", "react", "设计模式"]
console.log(newBook2.getBooks())// (5) ["javascript", "html", "css", "vue", "react"]