2013-05-14 3 views
0

Это проблема чистоты.Изменение поля на прототипе от дочернего объекта

Я использую прототипы для реализации базового наследования, чтобы сохранить мой код СУХОЙ, у меня есть несколько прототипов, которые предназначены для абстрактных целей и целей (не предполагается, что они когда-либо будут созданы за пределами того, чтобы быть установленными в качестве прототипов для другие объекты), и они содержат некоторый код, который вызовут «дочерние» объекты. Проблема в том, что функции в прототипе полагаются на некоторые поля прототипа. Обновление поля на дочернем объекте, очевидно, не изменяет поле прототипа. Я хочу избежать звонка

childObject.prototype.field = foo; 

как это становится грязным, чем глубже наследство.

Ниже я вставлял пример, который объясняет, что я пытаюсь сделать. Вы можете видеть, как он работает на jsfiddle here.

//Prints something once. 
function Printer(text) { 
    this.text = text || ""; 
    this.print = function() { 
     alert(text); 
    }; 
} 

//Prints everything a set number of times 
function AnnoyingPrinter(text, count) { 
    this.prototype = new Printer(text); 
    this.count = count || 1; 

    this.print = function() { 
     for (var i = 0; i < this.count; i++) { 
      this.prototype.print(); 
     } 
    }; 
} 

function doStuff() { 
    var annoyer = new AnnoyingPrinter("Hello world!", 2); 
    annoyer.print(); 
    //Now I want to change the text without having to dig down into the prototype  (particularly if I ever want to extend AnnoyingPrinter too) 
    annoyer.text = "Goodbye world!"; 
    annoyer.print(); 
} 

//Expected outcome: 
//Hello world! 
//Hello world! 
//Goodbye world! 
//Goodbye world! 


//Actual outcome: 
//Hello world! 
//Hello world! 
//Hello world! 
//Hello world! 
doStuff(); 
+0

Почему вы назначаете свойство с именем '.prototype' в конструкторе в первую очередь? Вот как работает прототипное наследование. –

+0

Думаю, я, должно быть, подобрал некоторые вредные привычки, изучая JS, каков правильный способ сделать это? –

+0

Вы хотите, чтобы «AnnoyingPrinter» наследовал от «Принтера»? Если это так, я соберу ответ, который показывает хороший образец. –

ответ

2

Это типичный шаблон для прототипного наследования.

function Printer(text) { 
    this.text = text || ""; 
} 
Printer.prototype.print = function() { 
    alert(this.text); 
} 

function AnnoyingPrinter(text, count) { 
    Printer.call(this, text); 
    this.count = count || 1; 
} 
AnnoyingPrinter.prototype = Object.create(Printer.prototype); 

AnnoyingPrinter.prototype.printAll = function() { 
    for (var i = 0; i < this.count; i++) { 
     this.print(); 
    } 
} 

Итак ваш doStuff() может пойти дальше и создать новый AnnoyingPrinter и вызвать print().

function doStuff() { 
    var annoyer = new AnnoyingPrinter("Hello world!", 2); 
    annoyer.printAll(); // "Hello world!" "Hello world!" 
    annoyer.text = "Goodbye world!"; 
    annoyer.printAll(); // "Goodbye world!" "Goodbye world!" 
} 

DEMO:http://jsfiddle.net/DhbgE/

Я просто должен был изменить его так, что эти два конструктора были разные имена методов. Если бы мы дали AnnoyingPrinter метод .print(), он затенял бы это из Printer.

+0

В стороне, мне кажется интересным, что MDN не использует этот шаблон: https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/call. У вас есть ссылка на преимущества/недостатки вашего ответа по сравнению с Ben336?(Отредактировано, потому что я забыл, что вы не можете нажать на комментарии) –

+0

Моя ошибка, похоже, они это делают. –

+1

@NickUdell: Ну, так вы делаете это, вы не пользуетесь наследованием. Каждый объект получает свои собственные функции, а не наследует их от общего объекта прототипа. Бывают моменты, когда имеет смысл это делать, но обычно это будет когда вы устанавливаете связь * "имеет ..." * вместо отношения * "является ..." *. –

1

Сохраните свойства на локальном объекте и укажите их в функции прототипа. Вы не хотите сохранять состояние в объекте прототипа, что должно быть действительно просто для функций (или, если необходимо, статических полей).

http://jsfiddle.net/C7aPQ/2/

//Prints something once. 
function Printer(text) 
{ 
    this.text = text || ""; 
    this.print = function() 
    { 
     alert(this.text); 
    }; 
} 

//Prints everything a set number of times 
function AnnoyingPrinter(text,count) 
{ 
    this.prototype = new Printer(text); 
    this.text = text; 
    this.count = count || 1; 

    this.print = function() 
    { 
     for(var i =0;i<this.count;i++) 
     { 
      this.prototype.print.call(this); 
     } 
    }; 
} 
+0

Почему вы дублируете свойство 'text'? – Ian

+0

Ах, я не думаю, что раньше сталкивался с методом .call. Это именно то, что я искал. Благодаря! –

+0

@Ian Потому что он все еще может захотеть создать принтер. Очевидно, это не идея дизайна. Если бы я это делал, я бы не стал вообще называть унаследованный метод или переименовал его. Но это был минимальный способ сделать его работу над дизайном. –

Смежные вопросы