提到代码重用,我们往往会想到inherits,当然还有其它的方式 ,我们还可以通过compoite来达到代码重用的目的。不过,当我们在试图实现代码重用的时候,请记住“Prefer object composition to class inheritance"
Classical Versus Modern Inheritance Patterns
在JS中什么是Classical继承呢,这个命名方式不是通用的。所表达的意思其实就是别的语言中的类继承。因为在JS中不存在类这个概念,我们可以随时改变它的属性和方法,而不必像其他语言那样麻烦。
在JS中,有constructor,还有可以用new来产生一个新的对象实例。我们首先定义一个constructor function,然后再new一下,是不是和JAVA, C#中的用类来生成object非常类似。所以这种方式就称为"classical" inherits, 而modern的继承方式就是指,我们不需要通过这种方式来实现的代码重用。
继承的目的就是当我们在实现一个子类对象的时候,我们希望这个子类对象可以继承父类的所有属性和方法。
//the parent construcotr function Parent(name){ this.name = name || 'Adam'; } //adding functionality to the prototype Parent.prototype.say = function() { return this.name; } //empty child construcotr function Child(name) {} //inhertiance ,我们将会在后面来实现 inherit(Child, Parent);
Classica Pattern #1 -- The Default Pattern
我们将最常用的方式就是我们把子类的prototype来设置成Parent()的一个实例。
function inherit(C, P) {
C.prototype = new P();
}
结合着上面的代码在前一个例子中,我们可以得到如下结果
var kid = new Child();
kid.say(); //"Adam"
Prototype Chain
当我们用prototype来实现继承的时候,我们不但会继承你类所有的属性和方法,还包括它的prototype的所有的属性和方法。
那这种机制如何来实现的,当们来创建一个new Parent()的时候,实际上我们创建了如下的一个对象(注意__proto__并不是一个标准属性,我们只是借用来示例一下)
new Parent();
name = Adam
__proto__ ---> Parent.prototype
say()
而当我们试图来访问say()时,请注意,现在我们的对象中并不含有这个方法,所以现在就会用到那个隐含的对象,__proto__它指向的是一个Parent()函数的prototype对象。
现在让我们来看一下new Child()的时候发生了什么:
new Child()
__proto__ ----> new Parent()
name = Adama
__proto__ ---> Parent.prototype
say()
Child() construcotr是空的,没有任何的属性,当我们试图用kid.say的时候,它会首先检查自己是不是有这个方法,没有,它会查找它的prototype,而它的prototype在检查的时候,它会去检查它的__proto__指向的prototype.
Classical Pattern #2 ---Rent-a-Constructor
看下面的代码:
function Child(a,c,b,d) {
Parent.apply(this, arguments);
}
通过这种方式,子类只会继承那些通过this添加的属性和方法,它不会继承那些添加到prototype对象中的那些属性。使用这种方式,子对象将会把那些属性全部复制到这个对象上,而不像使用prototype继承那样得到的仅仅是一个引用。
//a parent constructor function Article() { this.tags = ['js', 'css']; } var article = new Article(); //a blog post inhertis from an article object //via the prototype function BlogPost() { BlogPost.prototype = article; var blog = new BlogPost(); //a staic page inhertis from the article //via the rented constructor pattern function StaicPage() { Article.call(this); } var page = new StaticPage(); alert(article.hasOwnProperty('tags'));//true alert(blog.hasOwnProperty('tags'));//false alert(Page.hasOwnProperty('tags'));//true; //当我们改变tags的时候 blog.tags.push('html'); page.tags.push('php'); alert(article.tags.join(','));//"js,css, html" }
Multiple Inheritance by Borrowing Constructors
function Cat() { this.tags = 4; this.say = function() { return "meaowww"; } } function Bird() { this.wings = 2; this.fly = true; } function CatWings() { Cat.apply(this); Bird.apply(this); } var jane = new CatWings(); console.dir(jane); //fly true, lengs 4, wings 2, say function()
Classical Pattern #3 --Rent and Set Prototype
function Child(a,c,b,d) {
Parent.apply(this, arguments);
}
Child.prototype = new Parent();
这种模式的好处在于,它会复制父类的那些私有属性,还可以引用那些父类重用的代码
Classical Pattern #4 --Share the Prototype
function inherit(C,P) {
C.prototype = P.prototypel;
}
Classical Pattern #5 ---A Temporary Constructor
function inherit(C, P) {
var F= function() {};
F.prototype = P.prototype;
C.prototype = new F();
}
重设构造函数
为何要重设构造函数
function Parent() {} function Child(){} inherit(Child, Parent); var kid = new Child(); kid.constructor.name;//"Parent" kid.constructor === Parent;//true
在上面的代码中,我们可以看到当我们通过继承来生成一个子类的实例时,发现其中的construcotr被重写了,究其根源是因为,我们在设置prototype的时候,并没有重设constructor,因为constructor是prototype的一个属性,所以我们当通过重写prototype来实现继承时,一定要记住重写prototype的construcotr函数,请看下面修正好的结果:
function inherit(C,P){ var F = function(){}; F.prototype = P.prototype; C.prototype = new F(); C.uber = P.prototype; C.protoype.constructor = C; }
Klass
让我们来看一个实现的例子:
var klass = function(Parent, props) { var Child, F, i; //new constructor Child = function() { if(Child.uber && Child.uber.hasOwnProperty("__construct")){ Child.uber.__construct.apply(this, arguments); } if(Child.prototype.hasOwnProperty("__construct")){ Child.protoype.__construct.apply(this, arguments); } } //inherit Parent = Parent || Object; F = function() {} F.prototype = Parent.prototype; Child.prototype = new F(); Child.uber = Parent.prototype; Child.prototype.constructor = Child; //add implementation methods for(i in props){ if(props.hasOwnProperty(i)){ Child.protoype[i] = props[i]; } } return Child; };
Prototypal Inheritance
所谓的modern继承模式,就是不再有class出现。我们直接继承自其他的对象。你可以这样来想象一下,你有一个object,然后你想再创建一个对象,然后新建对象重用上一个对象的所有方法:
View Code
通过copy属性来完成继承
function extend(parent, child) { var i; child = child || {}; for(i in parent) { child[i] = parent[i]; } } var dad = {name: "Adam"}; var kid = extend(dad); kid.name;//"Adam"