@zlodiak

Как правильно применить паттерн Prototype?

Пытаюсь сгенерировать 20 однотипных объектов при помощи порождающего паттерна Prototype. Но в результате получаю 20 пустых объектов. Это видно по выводу их в консоль. Вот сам код:
var stars = [],
    starsCnt = 20;
    
var Helper = function() {
  this.randomIntFromZero = function(maxExclusive) {
      return Math.floor(Math.random() * (maxExclusive));
  };              
};  

var helper = new Helper();

var starPrototype = {
  xCoord: helper.randomIntFromZero(400),
  yCoord: 0,
  color: helper.randomIntFromZero(5),
  speed: helper.randomIntFromZero(10)
};    

function star() {
  function F() {};  
  F.prototype = starPrototype;  
  return new F();  
};

for(var i = 0; i < starsCnt; i++) {    
  stars.push(new star());
}  

console.log(stars);


JSFIDDLE здесь.

Проблема в том, что консоль выводит 20 пустых объектов(то есть в каждом их них нет свойств: xCoord, yCoord, color, speed)
  • Вопрос задан
  • 163 просмотра
Решения вопроса 1
  • @MaxKorz
    Для реализации паттерна Prototype вам нужен конструктор и prototype. У объекта starPrototype нет ни собственного конструктора, ни собственного prototype. Это просто объект с перечисленными свойствами, что нам не подходит.

    Перепишем код следующим образом:
    // Почему Helper реализован именно так - объяснено под кодом
    var Helper = function() {};
    Helper.prototype.randomIntFromZero = function(maxExclusive) {
        return Math.floor(Math.random() * (maxExclusive));
    };
    
    var helper = new Helper();
    
    // Реализуем родительский класс BaseStar
    var BaseStar = function() {
        // Прописываем все необходимые свойства в конструкторе
        this.xCoord = helper.randomIntFromZero(400);
        this.yCoord = 0;
        this.color = helper.randomIntFromZero(5);
        this.speed = helper.randomIntFromZero(10);
    };
    // Прописываем метод increaseYCoord в prototype BaseStar
    BaseStar.prototype.increaseYCoord = function() {
        this.yCoord += 1;
    };
    // Прописываем метод decreaseYCoord в prototype BaseStar
    BaseStar.prototype.decreaseYCoord = function() {
        this.yCoord -= 1;
    }
    
    // Реализуем дочерний класс Star
    var Star = function() {
       // Вызываем конструктор родителя в текущем контексте.
       // Таким образом свойства родителя попадают в дочерний класс
       BaseStar.apply(this, arguments);
    };
    // Наследуем прототип BaseStar
    Star.prototype = Object.create(BaseStar.prototype);
    // Но сохраняем конструктор
    Star.prototype.constructor = Star;
    
    
    var stars = [];
    for(var i = 0; i < 20; i++) {    
      stars.push(new Star());
    }  
    
    console.log(stars);
    console.log(stars[0].yCoord); // 0
    console.log(stars[1].yCoord); // 0
    console.log(stars[2].yCoord); // 0
    stars[0].increaseYCoord();
    stars[1].decreaseYCoord();
    console.log(stars[0].yCoord); // 1
    console.log(stars[1].yCoord); // -1
    console.log(stars[2].yCoord); // 0


    Запомните:
    1. Свойства всегда задаются в конструкторе через this. Они создаются заново в дочерних классах через apply.
    2. Методы всегда задаются в prototype. Они всегда наследуются через prototype, и не создаются заново.

    CPluCka.png

    Подробнее: https://learn.javascript.ru/class-inheritance

    Надеюсь понятно объяснил (и надеюсь нигде не ошибся)

    P.S. этот же код можно переписать в ES6 так:
    class Helper {
      randomIntFromZero(maxExclusive) {
        return Math.floor(Math.random() * (maxExclusive));
      }
    };
    
    const helper = new Helper();
    
    // Реализуем родительский класс BaseStar
    class BaseStar {
      constructor() {
        // Прописываем все необходимые свойства в конструкторе
        this.xCoord = helper.randomIntFromZero(400);
        this.yCoord = 0;
        this.color = helper.randomIntFromZero(5);
        this.speed = helper.randomIntFromZero(10);
      }
      
      increaseYCoord() {
        this.yCoord += 1;
      }
      
      decreaseYCoord() {
        this.yCoord -= 1;
      }
    }
    
    // Реализуем дочерний класс Star
    // Ключевое слово extends говорит о том,
    // что мы наследуемся от класса BaseStar
    // Под капотом происходит тоже самое, что мы делали выше
    // Вызывается apply для наследования свойств конструктора
    // И происходит ссылка на prototype
    class Star extends BaseStar {}
    
    const stars = [];
    for(let i = 0; i < 20; i++) {
      stars.push(new Star());
    }  
    
    console.log(stars);
    console.log(stars[0].yCoord); // 0
    console.log(stars[1].yCoord); // 0
    console.log(stars[2].yCoord); // 0
    stars[0].increaseYCoord();
    stars[1].decreaseYCoord();
    console.log(stars[0].yCoord); // 1
    console.log(stars[1].yCoord); // -1
    console.log(stars[2].yCoord); // 0

    В ES6 "class" это синтаксический сахар над кодом выше
    Ответ написан
Пригласить эксперта
Ответы на вопрос 1
  • rishatsharafiev
    @rishatsharafiev
    React/Redux, Django
    Так все правильно, объекты будут пустые, но в свойстве __proto__ будут лежать свойства прототипа5a6ead8b5b227655333700.png
    При вызове свойства, оно ищется в текущем объекте, потом в свойстве __proto__, далее в выше и выше
    class-inheritance-rabbit-animal.png
    Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы