Введение в ООП: Прототип

Здравствуйте! Прошу помочь закончить задачу:


Моё решение:

Money.prototype.constructor = function constructor(value, currency = 'usd'){
  this.value = value; 
  this.currency = currency;
}

Money.prototype.getValue = function getValue(){
  return this.value;
}

Money.prototype.exchangeTo = function exchangeTo(currency){
  const value = Money.prototype.getValue;
  return new Money(value, currency)
}

Money.prototype.add = function add(money){
  if(this.getValue == money.getValue){
    return new Money(this.getValue + money.getValue);
  }else{
    return new Money(this.getValue + money.exchangeTo(this.currency).getValue());
  }
}

Money.prototype.format = function format() {
        return this.getValue().toLocaleString("en-US",
            {
                style: 'currency',
                currency: this.currency === "usd" ? 'USD' : 'EUR',
            });
    }

Вывод тестов следующий:

ReferenceError: Money is not defined

      2 | // BEGIN (write your solution here)
      3 | 
    > 4 | Money.prototype.constructor = function constructor(value, currency = 'usd'){
        | ^
      5 |   this.value = value; 
      6 |   this.currency = currency;
      7 | }

      at Object.<anonymous> (Money.js:4:1)
      at Object.<anonymous> (__tests__/Money.test.js:3:1)

Ответ ментора на вопрос, почему Money не определён:

Ругается на то что Money не определен. Оно и верно, вы сразу стали прототипу “наращивать” методы, а сам Money никак не определили. Что это - переменная, функция, объект, класс?

Подскажите, как определить Money?
Пробовал этой строчкой: const money1 = new Money(100); - не сработало

Точно так же, как и любую другую переменную в JS. Ментор задал правильный вопрос: что такое Money?

Определил Money в качестве объекта:

 const Money = {};

Вывод тестов:

TypeError: Cannot set property 'constructor' of undefined

      2 | // BEGIN (write your solution here)
      3 | const Money = {};
    > 4 | Money.prototype.constructor = function constructor(value, currency = 'usd'){
        | ^
      5 |   this.value = value; 
      6 |   this.currency = currency;
      7 | }

      at Object.<anonymous> (Money.js:4:1)
      at Object.<anonymous> (__tests__/Money.test.js:3:1)

Тебе нужна функция которую ты будешь потом использовать как конструктор объектов.

Иными словами определи такую функцию

function Money(value, currency) {
// ...
}

прежде чем записывать методы в прототип.

В другой ветке я описывал как описывать классы через функцию конструктор и прототип:

Конструктор - это просто функция, которую вызвали с оператором new:

Написал:

function Money(value, currency = 'usd'){
  this.value = value; 
  this.currency = currency;
}

а что с кодом ниже делать?

Money.prototype.constructor = function constructor(value, currency = 'usd'){
  this.value = value; 
  this.currency = currency;
}

Он не нужен. Твой код решает ту же задачу что описана в этом коде. У меня даже подозрение что это псевдокод ибо никто не пишет в продакшене Money.prototype.constructor = function.

Я так и сделал и добавил скобки у getValue, здесь:

Money.prototype.exchangeTo = function exchangeTo(currency){
  const value = Money.prototype.getValue();
  return new Money(value, currency)
}

Вывод теста:
● Money

expect(received).toBe(expected) // Object.is equality

Expected: 70
Received: undefined

   6 |   const money1 = new Money(100);
   7 |   expect(money1.getValue()).toBe(100);
>  8 |   expect(money1.exchangeTo('eur').getValue()).toBe(70);
     |                                               ^
   9 |   const money2 = new Money(200, 'eur');
  10 |   expect(money1.getValue()).toBe(100);
  11 |   const money3 = money2.add(money1);

  at Object.<anonymous> (__tests__/Money.test.js:8:47)

То есть, ошибка пропала. Появилась другая… Кстати, если уберу скобки, то вывод теста будет:

 ● Money

    expect(received).toBe(expected) // Object.is equality

    Expected: 70
    Received: [Function getValue]

Возьми свой код решения, потом код из теста:

const money1 = new Money(100);
console.log(money1.exchangeTo('eur').getValue())

Ожидается некоторое значение, а получается undefined. Потом начинай копать почему.

Это не то что тебе нужно. В этом коде ты записываешь в value ссылку на функцию из прототипа конструктора Money.

Ты хочешь взять значение как число у инстанса. Как слышишь слово “инстанс” сразу задумываешься о this.

Исправил!
Теперь код такой:

function Money(value, currency = 'usd'){
  this.value = value; 
  this.currency = currency;
}

Money.prototype.getValue = function getValue(){
  return this.value;
}

Money.prototype.exchangeTo = function exchangeTo(currency){
  const value = this.getValue();
  return new Money(value, currency);
}

Money.prototype.add = function add(money){
  if(this.getValue == money.getValue){
    return new Money(this.getValue + money.getValue);
  }else{
    return new Money(this.getValue + money.exchangeTo(this.currency).getValue());
  }
}

Money.prototype.format = function format() {
        return this.getValue().toLocaleString("en-US",
            {
                style: 'currency',
                currency: this.currency === "usd" ? 'USD' : 'EUR',
            });
    }


И ошибка уже в разных значениях:

● Money

expect(received).toBe(expected) // Object.is equality

Expected: 70
Received: 100

   6 |   const money1 = new Money(100);
   7 |   expect(money1.getValue()).toBe(100);
>  8 |   expect(money1.exchangeTo('eur').getValue()).toBe(70);
     |                                               ^
   9 |   const money2 = new Money(200, 'eur');
  10 |   expect(money1.getValue()).toBe(100);
  11 |   const money3 = money2.add(money1);

  at Object.<anonymous> (__tests__/Money.test.js:8:47)

Эта проблема решается полумеханически:

  1. Смотришь на то какое значение ожидается в результате теста, и какое возвращает твой код. Если они разные то
  2. Прикидываешь на каком этапе от начала кода теста до его завершения промежуточное значение переменной отличается от ожидаемого. Например посмотри результат выполнения money1.exchangeTo('eur'). Соответствует ли объект твоим ожиданиям для тех входных данных с которыми вызывается код.
  3. Найди и почини причину несоответсвия. Тут важно быть готовым пересмотреть подход к решению в целом: не всегда мелких правок достаточно. И к шагу 1.

Из того что бросается в глаза (но не является причиной ошибки в тесте) - ты не вызываешь методы getValue, хотя нужно: