2013-06-23 4 views
1

Я склонен писать свои «классы» JavaScript в стиле c.Замена __proto__ прототипом

В C# (для примера) мы делаем это

public class Parent { 
    // stuff 
} 

public class Child : Parent { 
    // Protected and public stuff from Parent will be accessible 
} 

В JS я нашел эквивалент этого, используя прото, в примере

var namespace = namespace || {}; 

namespace.Parent = function() { 

    // Public variables 
    self.pubVariable = "I am accessible"; 

    // Private variables 
    var priVariable = "I'm not accessible outside of self"; 

    // ctor 
    function self() {} 

    return self; 
} 


namespace.Child = (function() { 
    this.__proto__ = new namespace.Parent(); 

    // ctor 
    function self() {} 

    self.init = function() { 
     // Prints the pubVariable 
     console.log(pubVariable); 
    }; 

    return self; 

})($); 

// Call it (statically)  
namespace.Child.init(); 

В то время как это работает это Webkit и Только Mozilla. Я понял, что это может быть достигнуто с использованием прототипа, но не может понять, как это сделать. Любые советы приветствуются. Благодаря!

+0

Почему вы используете IIFE для ребенка, но не для родителей? Кажется, что этот код никогда не будет работать. – Bergi

+2

Почему вы пишете классы в стиле с языка с совершенно другой системой классов? javascript классы МОГУТ заставить действовать «как» C (или классические) классы, но вы обнаружите, что намного легче прекратить работать против него и идти с потоком. – tkone

+0

@Bergi, код работает просто отлично в webkit/moz. Доступны все дочерние переменные и методы родителя. –

ответ

6

Для родителей/дочерних классов, я хотел бы сделать что-то вроде этого

// your parent class 
var Parent = function() { 

    // parent constructor 
    console.log("parent constructor!"); 

    // some public properties 
    this.foo = "foo"; 
    this.bar = "bar"; 

    // a private data member 
    var secret = "123456"; 
}; 

// a parent function 
Parent.prototype.something = function() { 
    console.log("something!"); 
} 

// your child class 
var Child = function() { 

    // call parent constructor 
    Parent.call(this);  

    // child constructor 
    console.log("child constructor!"); 

    // override bar 
    this.bar = "override!"; 
}; 

// the magic! 
// child prototype build from parent prototype 
Child.prototype = Object.create(Parent.prototype, {constructor: {value: Child}}); 

Пример использования

var c = new Child(); 
c.something(); 
// => parent constructor! 
// => child constructor! 
// => something! 

c.foo //=> "foo" 
c.bar //=> "override!" 

Если вы используете «разделяет пространства имен» понятие идентично.


Вы можете увидеть эту картину в ряде библиотек


EDIT

За ваши комментарий, вот и добавила демонстрацию

var Foo = function(){}; 
Foo.prototype.hello = function(){ return "hello!"; }; 
var foo = new Foo(); 

// call our hello method 
// this calls foo.__proto__.hello 
foo.hello(); //=> "hello!" 

// override `hello` method for this instance 
foo.hello = function(){ return "こんにちは"; }; 

// call our hello method again 
// this calls foo.hello because it is defined directly on our instance 
// (it has a higher precedence in the lookup chain) 
foo.hello(); //=> "こんにちは" 

// remove the override 
delete foo.hello; 

// call our hello method again 
// this goes back to calling foo.__proto__.hello 
foo.hello(); //=> "hello!" 

// remove the method prototype 
delete Foo.prototype.hello 

// call our hello method one last time 
// spoiler: it's gone! 
foo.hello(); //=> TypeError: Object [object Object] has no method 'hello' 

Как вы можете видеть, вы потеряете эту функцию, непосредственно определяющие методы на экземпляре с использованием this.something = function(){};. Я лично предпочитаю определять методы на прототипе из-за дополнительной гибкости. Таким образом, прототип действительно работает как план. Вы получаете все предопределенное поведение; вы можете изменить, если необходимо, и вернуться к оригиналу, когда захотите, все на основе каждого экземпляра.


ЕЩЕ ОДИН ВЕЩЬ

В нашем последнем примере мы имели метод прототипа и переопределение метода экземпляра. Есть ли способ вызвать оригинальный метод? Посмотрим!

var Foo = function(){}; 
Foo.prototype.hello = function(){ return "hello!"; }; 

var foo = new Foo(); 
foo.hello = function(){ return "こんにちは!"; } 

// call override method 
foo.hello(); //=> "こんにちは!" 

// call original method 
Foo.prototype.hello.call(foo); //=> "hello!" 

// japanese just one more time... 
foo.hello(); //=> "こんにちは!" 

Это тоже сработает, но у меня никогда не было необходимости. Я полагаю, что выгода вам не нужно знать исходный класс таким образом :)

// call original method from the instance 
foo.__proto__.hello.call(foo); //=> "hello!" 

Прототипы!

+0

+1; было бы неплохо, если бы вы могли немного прокомментировать частные переменные и общедоступные свойства и как они «унаследованы» – Bergi

+0

@Bergi, обновленный, чтобы включить такие свойства. – naomik

+1

Это отличная демонстрация прототипа, спасибо! –

1

Я думаю, вы хотите, чтобы это

// namespace 
var namespace = namespace || {}; 

// Parent Class 
namespace.Parent = function() { 
    this.pubVariable = "I am accessible"; 
    var priVariable = "I'm not accessible outside of this"; 
} 

// Child class 
namespace.Child = function() { 
    // namespace.Parent.call(this); 
    this.init = function() 
    { 
     // returns Parent class' pubVariable 
     // inherited by namespace.Child.prototype 
     return this.pubVariable; 
    } 
}; 

// inherit Parent class 
namespace.Child.prototype = new namespace.Parent(); 

var kid = new namespace.Child(); 
console.log(kid.init()); // I am accessible 

Если вы используете namespace.Parent.call(this) то Child класс будет иметь свою собственную копию pubVariable но теперь Child класс использует Родителя pubVariable.

Кроме того, если вы хотите поделиться methods от родительского класса с подразделами классов, то вы должны добавить методы в прототипе родительского класса, как этот

namespace.Parent = function() { //... } 
namespace.Parent.prototype.aMethodInParent = function(){ //... }; 

Итак, когда вы будете наследовать его в подклассе, как это

namespace.Child = function() { // ... }; 
namespace.Child.prototype = new namespace.Parent(); 

Другой Sub/Детский класс

namespace.AnotherChild = function() { // ... }; 
namespace.AnotherChild.prototype = new namespace.Parent(); 

В этом случае оба дочерних класса будут использовать тот же метод aMethodInParent() из своего родительского класса.

DEMO.

+0

Ровно!Это позволяет мне разделять подмножества методов и классов на свои собственные объекты, сохраняя при этом доступ к положительным свойствам родителя. –

+0

Справа. Если вы хотите совместно использовать методы с подклассом, тогда вы должны добавить методы в прототипе родительского класса. –

+0

Не используйте 'new' для наследования родительского класса! – Bergi