《JavaScript设计模式》章4--继承
类式继承
原型链
- 首先我们创建一个构造函数
创建实例属性要使用关键字this,类的方法应该被添加到其prototype对象中,通过new关键字可以创建该类的实例对象
/* Class Person. 下面这段代码是照着原文手打的*/
function Person(name) { //类名大写
this.name = name; //类的实例属性使用this
}
Person.prototype.getName = function() { //类的方法添加到prototype对象中
return this.name
}
- 创建一个继承Person的类
继承一个类比创建一个类要复杂很多,java中只需要一个extend关键字即可
js中没有extend关键字,每个对象都有一个prototype属性,这个属性要么指向一个对象,要么为null
在访问对象的某个成员时,如果该成员为见于当前对象,则会在该对象的prototype属性所指对象(其原型对象)中查找,沿着原型链以此类推,直到找到为止。
这意味着,在js中,一个类需要继承一个类的时候,只需要将子类的prototype属性指向超类的一个实例即可
/* Class Author. 下面这段代码是照着原文手打的*/
function Author(name, books){
Person.call(this, name)
this.books = books
}
function.prototype = new Person()
Author.prototype.constructor = Author
Author.prototype.getBooks = function() {
return this.books
}
上面这段代码解释一下:
首先,像Person一样创建一个构造函数,在构造函数中调用超类的构造函数;
然后,设置原型链
最后,重新执行constructor,由于在指定prototype为Person时,其constructor属性被抹除了
extend函数
为了简化类式继承,我们将上面的过程封装成一个函数extend
function extend(subClass, superClass) {
/*
F是一个空函数, 这里用它创建了一个对象实例插入到原型链中,避免创建超类的新实例
原因是,超累的构造函数可能有一些副作用,可能比较庞大,或者会执行一些大量的计算
*/
var F = function() {}
F.prototype = superClass.prototype
subClass.prototype = new F()
subClass.prototype.constructor = subClass
}
使用extend函数来重写之前的类式继承
/*extend function*/
function Person(name) {
this.name = name
}
Person.prototype.getName = function() {
return this.name
}
function Author(name, books) {
Person.call(this, name)
this.books = books
}
extend(Author, Person)
Author.prototype.getBooks = function() {
return this.books
}
可以看到,不用手动设置prototype和constructor属性,而是在声明类之后,在向子类的prototype对象中添加方法之前使用extend实现继承
但是,有个问题, 在声明Author类时,超类名Person被固化了
改进一下extend函数
/*extend function. improved.*/
function extend(subClass, superClass) {
var F = function () {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
/*这小段代码我看不懂。。
看懂了。。回来补上我又
这里的superclass是小写,是新增的一个属性,意思就是子类的superclass属性指向了超类的prototype对象*/
subClass.superclass = superClass.prototype;
if(superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
使用进阶版的extend函数
function Author(name, books) {
/*
这里的Author.superclass.constructor = Person.prototype.constructor,其实就是Person...
所以这一句其实就跟之前的Person.call(this, name)一样。。。
*/
Author.superclass.constructor.call(this, name)
this.books = books
}
extend(Author, Person)
Author.prototype.getBooks = function() {
return this.books
}
有点长,喝口水
原型式继承
首先, 用一个类的声明定义对象的结构
然后,实例化该类来创建一个新对象,
用这种方式创建的对象都有一套该类所有实例属性的副本,每个实例方法只存在一份,但每个副本拥有一个指向它的链接
/*Person prototype Object*/
var Person = {
name: 'default name',
getName: function() {
return this.name
}
}
var reader = clone(Person)
reader.getName() //'default name'
reader.name = 'xxx'
reader.getName() //'xxx'
/*Author prototype Object*/
var Author = clone(Person)
Author.books = [] //default value
Author.getBooks = function() {
return this.books
}
//创建Author实例
var author = clone(Author)
author.name= 'yancy'
/*如果在未给author.books定义新的初始值的情况下,执行author.push,则会影响Author.books,也就影响到了其他还未初始化books的实例对象*/
author.books = ['book1', 'book2'] //初始化books为新的数组并放入两个元素
author.getName()
author.getBooks()
clone函数
/*clone function .*/
function clone(object) {
function F() {}
F.prototype = object;
return new F;
}
首先创建了一个空函数F,然后将F的原型对象指向object
类式继承和原型式继承对比
两种继承范型都可互换使用,类式继承更符合大众规范,原型式继承更符合js特性,更为简洁。
掺元类
如果多个类中包含同一个函数,这些类逻辑上也不存在任何的继承关系,那么可以单独定义一个掺元类(mixin class)。然后通过某种手段把这个类中的方法扩充到其他需要用到这些方法的类。
/*Mixin class*/
var Mixin = function() {}
Mixin.prototype = {
serialize: function() {
var output = []
for(key in this) {
output.push(key+': '+this[key])
}
return output.join(',')
}
}
上面的代码声明了一个Mixin类,它有一个serialize方法,例如我们现在需要让Author类能够使用该方法。继承?但是它们逻辑上不存在任何的继承关系,况且没有这个必要去继承。
使用augment函数
augment(Author, Mixin)
var author = new Author('yancy', ['book1', 'book2'])
//现在author作为Author的实例对象,可以直接使用serialize方法
var serializedString = author.serialize()
了解一下augment函数
原理很简单,使用for...in遍历予类的prototype中的每一个成员,如果受类中不存在该成员,则添加到受类,存在则跳过继续处理下一个。代码如下:
/*Augment function */
function augment(receivingClass, givingClass) {
for(methodName in givingClass.prototype) {
if(!receivingClass.prototype[methodName]) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName]
}
}
}
上面的代码只是示例,具体可以根据实际情况改进,比如可以让予类方法直接覆盖受类方法等。

浙公网安备 33010602011771号