以前在准备面试的时候经常会看到面试题里有js实现继承的相关题,总觉得很少用的到,也没有太理解为什么要那么写,只是硬记下来了。最近在看《JavaScript设计模式》,里面有一节很详细的讲到了js继承,后面的模式里也经常用到,在这里记录下。
// 声明父类
function SuperClass() {
this.superValue = true
}
// 为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
return this.superValue
}
// 声明子类
function SubClass() {
this.subValue = false
}
// 继承父类
SubClass.prototype = new SuperClass()
// 为子类添加共有方法
SubClass.prototype.getSubClass = function () {
return this.subValue
}
类式继承需要将第一个类的实例赋值给第二个类的原型。类的原型对象的作用就是为类的原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问。而我们实例化一个父类时,新创建的对象复制了父类的构造函数内的属性与方法,并将原型__proto__指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法,并且这个新创建的对象可直接访问到父类原型对象上的属性与方法。
新创建的对象不仅仅可以访问父类原型上的属性和方法,同样也可以访问从父类构造函数中复制的属性和方法。
使用方法:
var instance = new SubClass()
console.log(instance.getSuperValue()) // true
console.log(instance.getSubClass()) // false
我们还可以通过instanceof来检测某个对象是否是某个类的实例。
console.log(instance instanceof SuperClass) // true
console.log(instance instanceof SubClass) // true
console.log(SubClass instanceof SuperClass) // false
这里可能大家会有疑问,为什么subClass继承了SuperClass,会得到false。
这里我们要知道:instanceof是判断前面的对象是否是后面类(对象)的实例,并不表示两者的继承。
这种类继承模式有2个缺点:
// 声明父类
function SuperClass(id) {
this.books = ['JavaScript', 'html', 'css']
this.id = id
}
// 父类声明原型方法
SuperClass.prototype.showBooks = function () {
console.log(this.books)
}
// 声明子类
function SubClass(id) {
// 继承父类
SuperClass.call(this, id)
}
var instance1 = new SubClass(10)
var instance2 = new SubClass(11)
instance1.books.push('设计模式')
console.log(instance1.books) // ["JavaScript", "html", "css", "设计模式"]
console.log(instance1.id) // 10
console.log(instance2.books) // ["JavaScript", "html", "css"]
console.log(instance2.id) // 11
SuperClass.call(this, id) 这条语句是构造函数式继承的精华。call这个方法可以更改函数的作用环境,因此在子类中,对superClass调用这个方法就是将子类中的变量在父类中执行一遍,由于父类中是给this绑定属性的,因此子类自然也就继承了父类的共有属性。
我们总结下上面两种模式的特点,类式继承是通过子类的原型prototype对父类实例化来实现的,构造函数式继承是通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的,在继承中同时做到这两点,即为组合继承。
// 声明父类
function SuperClass(name) {
// 值类型共有属性
this.name = name
// 引用类型共有属性
this.books = ['JavaScript', 'html', 'css']
}
// 父类原型共有方法
SuperClass.prototype.getName = function () {
console.log(this.name)
}
// 声明子类
function SubClass(name, time) {
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)
instance1.books.push('设计模式')
console.log(instance1.books) // ["JavaScript", "html", "css", "设计模式"]
instance1.getName() // js book
instance1.getTime() // 2014
var instance2 = new SubClass('js book', 2015)
console.log(instance2.books) // ["JavaScript", "html", "css"]
instance2.getName() // js book
instance2.getTime() // 2015
这种继承方法,子类的实例中更改父类继承下来的引用类型属性如books,不会影响到其他实例,并且子类实例化过程中又能将参数传递到父类的构造函数中,如name。
function inheritObject(o) {
function F() {}
F.prototype = o
return new F()
}
var book = {
name: 'js book',
alikeBook: ['css book, html book']
}
var newBook = inheritObject(book)
newBook.name = 'ajax book'
newBook.alikeBook.push('xml book')
var otherBook = inheritObject(book)
otherBook.name = 'flash book'
otherBook.alikeBook.push('as book')
console.log(book.name) // js book
console.log(book.alikeBook) // ["css book, html book", "xml book", "as book"]
console.log(newBook.name) // ajax book
console.log(newBook.alikeBook) // ["css book, html book", "xml book", "as book"]
console.log(otherBook.name) // flash book
console.log(otherBook.alikeBook) // ["css book, html book", "xml book", "as book"]
原型式继承是对类式继承的一个封装,其实其中的过渡对象就相当于类式继承中的子类,不过在原型式中作为一个过渡对象出现的,目的是为了创建要返回的新的实例化对象。
var book = {
name: 'js book',
alikeBook: ['css book', 'html book']
}
function createBook(obj) {
var o = new inheritObject(obj)
o.getName = function () {
console.log(name)
}
return o
}
寄生式继承就是对原型继承的第二次封装,并且在这第二次封装过程中对继承的方法进行了拓展,这样新创建的对象不仅仅有父类中的属性和方法,而且还添加新的属性和方法。
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
}
//定义父类
function SuperClass(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
// 定义父类原型方法
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 instance1 = new SubClass('js book', 2014)
var instance2 = new SubClass('css book', 2013)
instance1.colors.push('black')
console.log(instance1.colors) // ["red", "blue", "green", "black"]
console.log(instance2.colors) // ["red", "blue", "green"]
instance2.getName() // css book
instance2.getTime() // 2013