日期:2014-05-16  浏览次数:20391 次

JavaScript Patterns 读书笔记(五)

5.Inheritance Pattern

  • Classical Pattern #1—The Default Pattern
    ??? The default method most commonly used is to create an object using the Parent() constructor and assign this object to the Child()’s prototype. Here’s the first implementation of the reusable inherit() function:
    function inherit(C, P) {
    	C.prototype = new P();
    }
    
    ???? It’s important to remember that the prototype property should point to an object, not a function, so it has to point to an instance (an object) created with the parent constructor, not to the constructor itself. In other words, pay attention to the new operator, because you need it for this pattern to work.Later in your application when you use new Child() to create an object, it gets functionality from the Parent() instance via the prototype, as shown in the following example:
    var kid = new Child();
    kid.say(); // "Adam"
    
    ?Drawbacks When Using Pattern #1
    ??? One drawback of this pattern is that you inherit both own properties added to this and prototype properties. Most of the time you don’t want the own properties, because they are likely to be specific to one instance and not reusable.
    ??? Another thing about using a generic inherit() function is that it doesn’t enable you to pass parameters to the child constructor, which the child then passes to the parent. Consider this example:
    var s = new Child('Seth');
    s.say(); // "Adam"
    
    ?? This is not what you’d expect. It’s possible for the child to pass parameters to the parent’s constructor, but then you have to do the inheritance every time you need a new child, which is inefficient, because you end up re-creating parent objects over and over.

  • Classical Pattern #2—Rent-a-Constructor
    ??? This next pattern solves the problem of passing arguments from the child to the parent.It borrows the parent constructor, passing the child object to be bound to this and also forwarding any arguments:
    function Child(a, c, b, d) {
    	Parent.apply(this, arguments);
    }
    
    ??? This way you can only inherit properties added to this inside the parent constructor.You don’t inherit members that were added to the prototype.
    ??? Using the borrowed constructor pattern, the children objects get copies of the inherited members, unlike the classical #1 pattern where they only get references. The following example illustrates the difference:
    // a parent constructor
    function Article() {
    	this.tags = ['js', 'css'];
    }
    var article = new Article();
    
    // a blog post inherits from an article object
    // via the classical pattern #1
    function BlogPost() {}
    BlogPost.prototype = article;
    var blog = new BlogPost();
    
    // note that above you didn't need `new Article()`
    // because you already had an instance available
    // a static page inherits from article
    // via the rented constructor pattern
    function StaticPage() {
    	Article.call(this);
    }
    var page = new StaticPage();
    
    alert(article.hasOwnProperty('tags')); // true
    alert(blog.hasOwnProperty('tags')); // false
    alert(page.hasOwnProperty('tags')); // true
    
    ?
    ?? In this example the child blog object modifies the tags property, and this way it also??? ??? modifies the parent because essentially both blog.tags and article.tags point to the same array. Changes to page.tags don’t affect the parent article because page.tags is a separate copy created during inheritance.

    Multiple Inheritance by Borrowing Constructors
    ??? Using the borrowing constructors patterns, it’s possible to implement multiple inheritance simply by borrowing from more than one constructor:
    function Cat() {
    	this.legs = 4;
    	this.say = function () {
    		return "meaowww";
    	}
    }
    
    function Bird() {
    	this.wings = 2;
    	this.fly = true;
    }
    
    function CatWings() {
    	Cat.apply(this);
    	Bird.app