Javascript Patterns--读书笔记6 (Code Reuse)

提到代码重用,我们往往会想到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的继承方式就是指,我们不需要通过这种方式来实现的代码重用。 

继承的目的就是当我们在实现一个子类对象的时候,我们希望这个子类对象可以继承父类的所有属性和方法。

View Code
//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继承那样得到的仅仅是一个引用。

View Code
//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

View Code
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();
}

重设构造函数

为何要重设构造函数

View Code
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

让我们来看一个实现的例子:

View Code
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属性来完成继承 

View Code
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"

 

posted @ 2012-09-25 21:36  moonreplace  阅读(380)  评论(0编辑  收藏  举报