2009-10-29 8 views
16

私はTDD JavaScriptでebook on GitHubを使っています。普及している継承パターンがないかと思います。あなたが追加のパターンを知っているなら、私はそれらを見たいと思っています。彼らは、次のことを持っている必要があります。よくあるJavaScriptの継承パターン

  1. 時間はテスト -
  2. ソースコードが提供されなければならない実際のアプリケーションで使用されます。できるだけまっすぐ進むべきであり、できるだけ賢明でなければならない。
  3. もちろん、正しく動作していること。

私がこれをやっている理由は、JavaScriptのオブジェクト継承が私たちの多くにとって非常に困難だったようです。私のJavaScriptの継承の章は、基本的にはCrockfordのGood PartsとWeb開発者向けのZakasのProfessional JavaScriptに対する学習支援ツールです。ここで

は、私がこれまで持っているパターンです。

// Pseudoclassical Inheritance 
    function Animal(name) { 
     this.name = name; 
     this.arr = [1,2,3]; 
    }; 
    Animal.prototype = { 
     constructor: Animal, 
     whoAmI: function() { return "I am " + this.name + "!\n"; } 
    }; 

    function Dog(name, breed) { 
     this.name = name; 
     this.breed = breed; 
    }; 
    Dog.prototype = new Animal(); 
    Dog.prototype.getBreed = function() { 
     return this.breed; 
    }; 
    Dog.prototype.bark = function() { 
     return 'ruff ruff'; 
    }; 

    // Combination Inheritance 
    function Parent(name) { 
     this.name = name; 
     this.arr = [1,2,3]; 
    }; 
    Parent.prototype = { 
     constructor: Parent, 
     toString: function() { return "My name is " + this.name; } 
    }; 
    function Child(name, age) { 
     Parent.call(this, name); 
     this.age = age; 
    }; 

    Child.prototype = new Parent(); 

    Child.prototype.getAge = function() { 
     return this.age; 
    }; 

    // Prototypal Inheritance 
    var helper = { // Thanks to Bob Vince for reminding me NOT to clobber Object! 

     inherit: function(p) { 
     NewObj = function(){}; 
     NewObj.prototype = p; 
     return new NewObj(); 
     }, 
     inheritPrototype: function(subType, superType) { 
     var prototype = helper.inherit(superType.prototype); 
     prototype.constructor = subType; 
     subType.prototype = prototype; 
     } 
    }; 

    function SubType(name, age) { 
     Parent.call(this, name); 
     this.age = age;  
    }; 
    //Child.prototype = new Parent(); // Gets replaced by: 
    helper.inheritPrototype(SubType, Parent); 
    SubType.prototype.getAge = function() { 
     return this.age; 
    }; 

    // Functional - Durable Pattern 
    function super_func(blueprint) { 
     var obj = {}; 
     obj.getName = function() { return blueprint.name; }; 
     obj.getAge = function() { return blueprint.age; }; 
     obj.getFoo = function() { return blueprint.foo; }; 
     obj.getBar = function() { return blueprint.bar; }; 
     return obj; 
    }; 
    function sub_func(blueprint) { 
     blueprint.name = blueprint.name || "Crockford's Place"; 
     supr = super_func(blueprint); 
     supr.coolAugment = function() { return "I give a fresh new perspective on things!" }; 
     return supr;  
    }; 

そして、興味のある人のために、ここでjspecテスト(申し訳ありませんが、値下げや、彼らは形式を少しマングルを使用しているものは何でも)は、次のとおりです。

describe 'JavaScript Inheritance Tests' 
    before_each 
    animal = new Animal("Onyx") 
    dog = new Dog("Sebastian", "Lab") 

    person = { password : 'secret', toString : function(){ return '<Person>' } } 
    stub(person, 'toString').and_return('Original toString method!')  
    end 
    describe 'Pseudoclassical Inheritance Creation' 
    it 'should create parent and child object using pseudoclassical inheritance' 
     animal.constructor.should.eql Animal 
     // dog.constructor.should.eql Dog // Nope: expected Animal to eql Dog 
     dog.constructor.should.eql Animal 
     animal.should.be_a Animal 
     dog.should.be_a Animal 
     // dog.should.be_a Dog // Nope! We severed the original prototype pointer and now point to Animal! 
     dog.should.be_an_instance_of Animal 
     dog.should.be_an_instance_of Dog 
     (animal instanceof Dog).should.be_false 
    end 
    it 'should behave such that child inherits methods and instance variables defined in parent' 
     animal.whoAmI().should.match /I am Onyx.*/ 
     dog.whoAmI().should.match /Sebastian.*/ 
     animal.should.respond_to 'whoAmI' 
     dog.should.respond_to 'whoAmI' 
     dog.should.have_prop 'name' 
    end 
    it 'should behave such that methods and instance variables added to child are NOT available to parent' 
     dog.bark().should.match /Ruff Ruff/i 
     dog.should.have_property 'breed' 
     dog.should.respond_to 'bark' 
     // animal.should.have_prop 'breed' // Of course not! 
     // animal.should.respond_to 'bark' // Of course not! 
    end 
    it 'should behave such that reference variables on the parent are "staticy" to all child instances' 
     dog.arr.should.eql([1,2,3]) 
     dog.arr.push(4) 
     dog.arr.should.eql([1,2,3,4]) 
     spike = new Dog("Spike", "Pitbull") 
     spike.arr.should.eql([1,2,3,4]) 
     spike.arr.push(5) 
     rover = new Dog("Rover", "German Sheppard") 
     spike.arr.should.eql([1,2,3,4,5]) 
     rover.arr.should.eql([1,2,3,4,5]) 
     dog.arr.should.eql([1,2,3,4,5]) 
    end 
    end 

    describe 'Combination Inheritance Solves Static Prototype Properties Issue' 
    it 'should maintain separate state for each child object' 
     child_1 = new Child("David", 21) 
     child_2 = new Child("Peter", 32) 
     child_1.arr.push(999) 
     child_2.arr.push(333) 
     child_1.arr.should.eql([1,2,3,999]) 
     child_2.arr.should.eql([1,2,3,333]) 
     child_1.getAge().should.eql 21 
     child_1.should.be_a Parent 
    end 
    end 

    describe 'Prototypal Inheritance' 
    it 'should inherit properties from parent' 
     person.toString().should.match /Original toString.*/i 
     person.password.should.eql 'secret' 
     joe = helper.inherit(person) 
     joe.password.should.eql 'secret' 
     joe.password = 'letmein' 
     joe.password.should.eql 'letmein' 
     person.password.should.eql 'secret' 
    end 
    end 

    describe 'Parisitic Combination Inheritance' 
    it 'should use inheritPrototype (to call parent constructor once) and still work as expected' 
     sub = new SubType("Nicholas Zakas", 29) 
     sub.toString().should.match /.*Nicholas Zakas/ 
     sub.getAge().should.eql 29 
     charlie = new SubType("Charlie Brown", 69) 
     charlie.arr.should.eql([1,2,3]) 
     charlie.arr.push(999) 
     charlie.arr.should.eql([1,2,3,999]) 
     sub.arr.should.eql([1,2,3]) 
     sub.should.be_an_instance_of SubType 
     charlie.should.be_an_instance_of SubType 
     (sub instanceof SubType).should.eql true 
     (sub instanceof Parent).should.eql true 
    end 
    end 

    describe 'Functional Durable Inheritance' 
    it 'should hide private variables' 
     sup = new super_func({name: "Superfly Douglas", age: 39, foo: "foo", bar: "bar"}) 
     sup.getName().should.eql 'Superfly Douglas' 
     sup.name.should.be_undefined 
     sup.getAge().should.eql 39 
     sup.age.should.be_undefined 
     sup.getFoo().should.eql 'foo' 
     sup.foo.should.be_undefined 
    end 

    it 'should create a descendent object that inherits properties while maintaining privacy' 
     sub = new sub_func({name: "Submarine", age: 1, foo: "food", bar: "barfly"}) 
     sub.getName().should.eql 'Submarine' 
     sub.name.should.be_undefined 
     sub.getAge().should.eql 1 
     sub.age.should.be_undefined 
     sub.getFoo().should.eql 'food' 
     sub.foo.should.be_undefined 
     sub.getBar().should.eql 'barfly' 
     sub.bar.should.be_undefined 
     sub.coolAugment().should.match /.*fresh new perspective.*/ 
     //sub.should.be_an_instance_of super_func NOPE! 
     //sub.should.be_an_instance_of sub_func NOPE! 
     sub.should.be_an_instance_of Object 
    end 
    end 

end 

ありがとうございます!ああ、あなたが私のエッセイ/本をチェックしたいのであれば、フィードバックを得たいと思っています。 TDD JavaScript at GitHub repo

+0

私は多くの異なるプロジェクトで多くのテクニックを試しました。私のお気に入りはObject.create()を使って基本プロトタイプをインスタンス化しています。私のブログをチェックしてください:http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

答えて

8

How to "properly" create a custom object in JavaScript?を参照してください。 (!私はそれを入力するので、多くの時間を無駄にするので、同様、それをリンクする可能性があります)

この:

Dog.prototype =新しい動物();

は一般に避けられます。あなたは例/チュートリアルのコードでそれを見ることができますが、それはインスタンス上のクラスを基底としているためにひどい混乱であり、障害のある方法で構築されたインスタンスです:nameは未定義です。もっと複雑なコンストラクタは、そのようなことで動揺するだろう。

Object.prototype.inherit =

構築するためのより良いアプローチですが、Objectに何のプロトタイプを作成することは非常に悪い味と考えられています。それは、オブジェクトを使い慣れていない地図として使用したり、他のコードを壊したりする危険性があります。このヘルパー関数は他の場所に置くことができます。 Function.prototype.subclass

(Firefoxやいくつかの他のブラウザに実装され、そうでないIEのJScriptの)constructorはJavaScriptで特別な意味を持っているので、私は、避ける傾向にあるでしょうprototype.constructor

個人的に

、その意味ではありませんここではconstructorは何もしません。それは混乱し、ほとんど常に最高の回避されます。したがって、クラスシステムのインスタンスにコンストラクタ関数へのリンクを含める場合は、別の名前を付けることをお勧めします。

+0

あなたのフィードバックに感謝します。私はObject.prototype.inheritについて同意します。私はこれを私のエッセイで言及していますが、少なくともコードのコメントに追加したり、ラッパー関数に入れたりします。それをキャッチするためにありがとう! Dog.prototype = new Animal()私は後のパターンで選択肢を与えます。あなたの入力ボブに感謝します。 – Rob

+0

私は先に進んでオブジェクトを壊すのではなく、ヘルパーオブジェクトを使用するようにコードを更新しました(そして上のコードリストを編集しました)。 – Rob

+0

うん、私は戻って私のエッセイを読んで、私はこう言う:「あなたが望むなら、そのような機能を返すラッパーを作成することができます。ちょうど怠惰な!!!私に "正しいこと"を作ってくれてありがとう! – Rob

0

dev/web/stuffフォルダーには、さまざまな継承パターンが少なくとも半ダースの実装されていますが、ほとんどのおもちゃです。

私は実際に時々使用すると、継承を容易にするためにはJavaScriptのデフォルトの疑似クラスベースのアプローチに比べて次のように薄いラッパーです:

Function.prototype.derive = (function() { 
    function Dummy() {} 
    return function() { 
     Dummy.prototype = this.prototype; 
     return new Dummy; 
    }; 
})(); 

例コード:

function Pet(owner, type, name) { 
    this.owner = owner; 
    this.type = type; 
    this.name = name; 
} 

Pet.prototype.toString = function() { 
    return this.owner + '\'s ' + this.type + ' ' + this.name; 
}; 

function Cat(owner, name) { 
    Pet.call(this, owner, 'cat', name); 
} 

Cat.prototype = Pet.derive(); 

var souris = new Cat('Christoph', 'Souris'); 

もう一つの興味深い1であります次のように、工場のメソッドを適切なプロトタイプのアプローチに自動的に追加します。

var Proto = new (function() { 
    function Dummy() {} 

    this.clone = function() { 
     Dummy.prototype = this; 
     return new Dummy; 
    }; 

    this.init = function() {}; 

    this.create = function() { 
     var obj = this.clone(); 
     this.init.apply(obj, arguments); 
     return obj; 
    }; 
}); 

例コード:

var Pet = Proto.clone(); 

Pet.init = function(owner, type, name) { 
    this.owner = owner; 
    this.type = type; 
    this.name = name; 
}; 

Pet.toString = function() { 
    return this.owner + '\'s ' + this.type + ' ' + this.name; 
}; 

Cat = Pet.clone(); 

Cat.init = function(owner, name) { 
    Pet.init.call(this, owner, 'cat', name); 
}; 

// use factory method 
var filou = Cat.create('Christoph', 'Filou'); 

// use cloning (the proper prototypal approach) 
var red = filou.clone(); 
red.name = 'Red'; 

あなたは私のimplementation of classesalready seenました。

+0

こんにちはChristoph!ありがとう。だから、あなたの派生メソッドは実際には継承メソッドと同じですが、それはクロージャを返して「それ自身」を呼び出すこと以外は同じです。パターンはコンストラクタを盗むこともあります - 基本的にコンステレーションパターン(プロトタイプとコンストラクタを組み合わせたもの)だと思います。私はもう少し長いパターンを見詰める必要があるでしょう - 私は今夜それを試してみると思います;-)クリストフを分かち合いに感謝します。 – Rob

1

私の以前の同僚の同僚は、javaのような継承を行うライブラリを開発しました。私はそれがRajendraの提案よりもセクシーだと思っています。

私はそれにアプローチするさまざまな方法を示す記事を書いたが、既知の悪い習慣が回避されていることを確認した。 http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.htmlこれは、ライブラリをダウンロードするのではなく、必要な作業を行うために改善できるコードをコピーしてコピーしたい場合です。

1

ここで言及する価値のあるパターンがあります。JavaScriptコンストラクタはオブジェクトを返すことがあります(必ずしもそうではありません)。this)。 "実際の"インスタンスオブジェクトの "実際の"メソッドにプロキシメソッドを含むプロキシオブジェクトを返すコンストラクタ関数を作成することができます。これは複雑に聞こえるかもしれませんが、そうではありません。ここでのコードスニペットです:

var MyClass = function() { 
    var instanceObj = this; 
    var proxyObj = { 
     myPublicMethod: function() { 
      return instanceObj.myPublicMethod.apply(instanceObj, arguments); 
     } 
    } 
    return proxyObj; 
}; 
MyClass.prototype = { 
    _myPrivateMethod: function() { 
     ... 
    }, 
    myPublicMethod: function() { 
     ... 
    } 
}; 

いいところは、私たちが保護されたメソッドの命名規則を定義する場合、プロキシの作成を自動化することができるということです。私はちょうどこれを行う小さな図書館を作成しました:http://idya.github.com/oolib/

0

ここに遅れていますが、私は2点を作っています。

1)スーパータイプオブジェクトの作成を継承するようにユーザーに通知しないでください。これはいくつかの理由で悪い習慣とみなされます。まず、それは原則間違いです。インスタンスをインスタンス化するのは、メソッドを使用するだけで、インスタンス自体で何も実行しないことです。これを行った正しい方法は、Object.prototype.inheritメソッドを使用することです。さらに、このメソッドでは、スーパータイプのコンストラクタ関数の引数を空にしておく必要があります。厳密な状況では、エラーが発生する可能性があります。

2)コンストラクタのスティールパターンについて言及していません。

function Supertype(name){ 
this.name=name; 
this.sayName = function(){console.log(this.name);}; 
} 

function Subtype(name){ 
//inherit by using (stealing) supertype constructor function 
Supertype(name); 

// child specific properties 
// 
} 
関連する問題