Een subtiele fout die ik heb gemaakt met constructeurs

Waarin ik bijna geen fout opmerkte

Ik kon een subtiele maar belangrijke fout opsporen die ik maakte bij het gebruik van constructors met subklassen en het handmatig instellen van prototypes.

Beschouw de volgende code (van een implementatie van steen-papier-schaar):

function Player(){
  this.move = null;
}
Player.prototype.setMove = function(mv) {
  this.move = mv;
};

function HumanPlayer() {

}
HumanPlayer.prototype = new Player();

let player1 = new HumanPlayer();
let player2 = new HumanPlayer();

player1.setMove('paper');
console.log(player1.move, player2.move);
//paper null

Hoewel de fout hier uiteindelijk geen groot probleem was, let op waar de eigenschap move van player1 en player2 aanvankelijk is opgeslagen - het object waarnaar wordt verwezen door HumanPlayer.prototype - en dit object wordt gedeeld door alle HumanPlayer objecten!

Beide spelers hebben toegang tot hun move eigendom van Human.prototype - wat betekent dat ze this.move share delen ! Laten we dit bevestigen door de waarde van move . te controleren met behulp van getPrototypeOf()

console.log(player1.hasOwnProperty('move'));
//false
console.log(Object.getPrototypeOf(player1).move);
//null
Object.getPrototypeOf(player2).move = 'paper';
console.log(player1.move);
//paper

Niet alleen player1 geen eigen verhuizing hebben, instelling player2.[[Prototype]].move tot paper is toegankelijk via player1.move ! We hebben eigenlijk geen move gedefinieerd als individuele status voor elke instantie van HumanPlayer

Vreemd genoeg werkte het programma prima - denk aan de setMove() functie:

Player.prototype.setMove = function(mv) {
  this.move = mv;
};

Bij het aanroepen van deze functie met player1.setMove('paper') , this verwijst naar speler1. Aangezien speler1 geen eigen eigenschap heeft move , een is gemaakt! Elke speler roept setMove() , elk heeft nu zijn eigen move eigenschap, en de move op HumanPlayer.prototype wordt nooit meer gebruikt.

player1.setMove('rock');
player2.setMove('paper');
console.log(player1.move, player2.move);
//rock paper
console.log(Object.getPrototypeOf(player1).move);
//null

We hebben geluk gehad - deze keer. Hoe dit goed op te lossen?

function Player(){
  this.move = null;
}

function HumanPlayer() {
  Player.call(this);
}
HumanPlayer.prototype = new Player();

let player1 = new HumanPlayer();
let player2 = new HumanPlayer();

console.log(player1.hasOwnProperty('move'));
//true

Focus op de HumanPlayer constructor - we hebben een aanroep toegevoegd aan de Player aannemer. Nu een nieuwe HumanPlayer wordt gemaakt, wordt de Player-constructor aangeroepen (met behulp van de context van het object dat eerst is gemaakt vanwege new ), en stelt de move . in eigendom van dit object. Nu heeft elke speler zijn eigen eigenschappen. Alles is goed met de wereld.