2009-10-29 4 views
16

Я работаю над ebook on GitHub на TDD JavaScript, и мне интересно, не хватает ли мне каких-либо популярных шаблонов наследования. Если вы знаете какие-либо дополнительные шаблоны, я бы с удовольствием их видел. Они должны иметь следующее:Популярные шаблоны наследования JavaScript

  1. Время испытания - используется в реальных приложениях
  2. Исходный код должен быть поставлены. Должна быть как можно более прямой и педантичной.
  3. Конечно, правильно и правильно.

Причина, по которой я делаю это, заключается в том, что кажется, что наследование объектов в JavaScript было довольно сложным для многих из нас. моя глава наследования JavaScript в основном является учебным пособием: «Хорошие детали Крокфорда» и Профессиональный JavaScript для веб-разработчиков Zakas.

Вот узоры у меня до сих пор:

// 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 тесты (извините, но Markdown или что-то, что они используют Mangles Формат немного):

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.

prototype.constructor

Лично я, как правило, чтобы избежать, потому что constructor имеет особое значение в JavaScript (как это реализовано в Firefox и некоторых других браузеров, не для JScript в IE), и это значение не что constructor делает здесь и то, что вы ожидаете от такого имущества; это запутанно и почти всегда лучше всего избегать. Поэтому, если включить ссылку на функцию конструктора в экземпляре в системе класса, я бы предпочел назвать ее чем-то другим.

+0

Благодарим за отзыв. Я согласен с Object.prototype.inherit, и я думаю, что упоминаю об этом в своем эссе, но я, по крайней мере, добавлю его к комментариям к коду и/или поставлю его в функцию обертки. Спасибо, что поймали это! Dog.prototype = new Animal() Я даю альтернативы в более поздних моделях. Спасибо за ваш вклад Боб. – Rob

+0

Я пошел дальше и обновил код (и отредактировал приведенный выше список кодов), чтобы использовать вспомогательный объект, а не clobbering Object. – Rob

+0

Yup, я вернулся и прочитал свое эссе, и я говорю: «Конечно, нет ничего, что требовало бы от вас объекта Monkey patch Object - вы могли бы создать оболочку, которая возвращает такую ​​функциональность, если вы пожелаете». Просто ленивый !!! Спасибо, что заставило меня «сделать это правильно»! – 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'); 

Еще один интересный один является следующий, который автоматически добавляет методы завода к надлежащему прототипному подходу:

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'; 

Вы имеете already seen моей implementation of classes.

+0

Привет, Кристоф! Благодарю. Таким образом, ваш метод вывода действительно такой же, как мой метод наследования, за исключением того, что он возвращает замыкание и вызывает «сам»; шаблон также использует кражу конструктора - я думаю, что это по сути комбинационный шаблон наследования (прототип и кража конструктора вместе взятые). Мне нужно будет смотреть на другой рисунок немного дольше - я думаю, что я буду играть с ним сегодня вечером и отчитываться ;-) Спасибо за то, что вы делитесь с Кристофом. – Rob

1

Сотрудник моей предыдущей компании разработал библиотеку, чтобы сделать java как наследование http://www.uselesspickles.com/class_library/. Я думаю, что это сексуальнее, чем предложения Раджендры, синтаксис выглядит чище.

Я написал статью, которая демонстрирует различные способы приближения к ней, но следя за тем, чтобы избежать известных вредоносных практик. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html, это если вы не хотите загружать библиотеку, а просто хотите скопировать какой-нибудь код, который вы можете улучшить, чтобы делать то, что вам нужно.

1

Здесь стоит отметить интересный шаблон: конструктор JavaScript может возвращать любой объект (не обязательно это). Можно было бы создать функцию-конструктор, которая возвращает прокси-объект, который содержит прокси-методы для «реальных» методов «реального» экземпляра объекта. Это может показаться сложным, но это не так; вот фрагмент кода:

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 
// 
} 
Смежные вопросы