5

Скажем, у меня есть объект игрока:JS Prototypal Inheritance: childs используют одни и те же родительские свойства?

var player = function(name) { 
    this.handlers = {}; 
} 

player.prototype.on = function(event, callback) { 
    if (!this.handlers[event]) { 
     this.handlers[event] = []; 
    } 
    this.handlers[event].push(callback); 
} 

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

var testPlayer = function(name) { 
    this.name = name; 
}; 

testPlayer.prototype = new player(); 

Теперь, когда я создаю testPlayer «S, каждый из них одни и те же handlers свойство:

var adam = new testPlayer('Adam'); 
adam.on('test', function(){}); 

var eve = new testPlayer('Eve'); 
// eve.handlers == {'test':<function>} 

Что я здесь отсутствует? Я понимаю, что каждый прототип testPlayer - это тот же самый объект new player, который я создаю при описании дочернего класса. Но есть ли какой-то способ для всех testPlayers иметь собственный набор обработчиков?

ответ

5

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

var testPlayer = function(name) { 
    this.name = name; 
    this.handlers = {}; 
}; 

testPlayer.prototype = new player(); 

Другим решением было бы создать это теневое свойство по требованию, от вашего on метода:

player.prototype.on = function(event, callback) { 
    // Check for a handlers property on the instance 
    if(!this.hasOwnProperty('handlers') { 
     this.handlers = {}; 
    } 
    if (!this.handlers[event]) { 
     this.handlers[event] = []; 
    } 
    this.handlers[event].push(callback); 
} 

Интересного факта

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

+1

+1 за "другое решение", это выглядит как самый годный к употреблению. –

+0

Я согласен, это вполне допустимая парадигма программирования. Досадной частью является то, что вы должны добавить такую ​​проверку в каждый метод, который использует 'handlers'. – MaxArt

+0

У вас может быть один метод init из конструктора и сделать это оттуда. Обратите внимание, что это только проблема, если вы изменяете свойства объекта (или массива) на прототипе. Если вы попытаетесь присвоить свойства, которые живут в прототипе, будет создано локальное свойство теневого копирования (вы не можете назначать свойства прототипа из экземпляров). – bfavaretto

1

Проблема состоит в том, что handlers является собственностью, которая была добавлена ​​в конструкторе, поэтому, когда вы делаете

testPlayer.prototype = new player(); 

вы добавляете каждое свойство нового player объекта testPlayer.prototype, и что содержитhandlers.

Итак, есть handlers свойства в каждом testPlayer объекта, а при добавлении свойства handlers вы добавляете свойство к объекту в прототипе, а не от testPlayer объекта.

Короче говоря, при вызове метода on вы добавляете свойство testPlayer.prototype.handlers, не adam.handlers или eve.handlers.

Чтобы обезопасить себя, определить:

var testPlayer = function(name) { 
    this.name = name; 
    this.handlers = {}; 
};