skip to Main Content

I am trying to write a function that prints out a negative value after it’s been run but I keep getting just the number.

const studentHolberton = (() => {
  let privateScore = 0;
  let name = null;

  function changeScoreBy(points) {
    privateScore += points;
  }

  return {
    setName: (newName) => {
      name = newName;
    },
    rewardStudent: () => {
      changeScoreBy(1);
    },
    penalizeStudent: () => {
      changeScoreBy(-1);
    },
    getScore: () => {
      return `${name}: ${privateScore}`;
    },
  };
})();

const jerry = Object.create(studentHolberton);
jerry.setName('Jerry');

//reward harry 4times
jerry.rewardStudent();
jerry.rewardStudent();
jerry.rewardStudent();
jerry.rewardStudent();

console.log(jerry.getScore());


const dray = Object.create(studentHolberton);
dray.setName('Dray');

// Reward Draco one time and penalize three times
dray.rewardStudent();
dray.penalizeStudent();
dray.penalizeStudent();
dray.penalizeStudent();

console.log(dray.getScore());

The expected output is
Jerry: 4
Dray: -2

but I am getting
Jerry: 4
Dray: 2

4

Answers


  1. Object.create creates a new object that uses the argument as the prototype for it.

    In your particular case, this is pointless because you aren’t storing any data on the object.

    All the data is stored in closed over variables so let privateScore is shared between both the harry and draco objects.


    To use the revealing module pattern for this, you’d need to:

    1. Assign the function that is currently an IIFE to studentHogwarts
    2. Call that function instead of Object.create(...) to create a new instance.
    const createStudent = () => {
      let privateScore = 0;
      let name = null;
    
      function changeScoreBy(points) {
        privateScore += points;
      }
    
      return {
        setName: (newName) => {
          name = newName;
        },
        rewardStudent: () => {
          changeScoreBy(1);
        },
        penalizeStudent: () => {
          changeScoreBy(-1);
        },
        getScore: () => {
          return `${name}: ${privateScore}`;
        },
      };
    };
    
    const alice = createStudent();
    alice.setName('Alice');
    
    //reward alice 4times
    alice.rewardStudent();
    alice.rewardStudent();
    alice.rewardStudent();
    alice.rewardStudent();
    
    console.log(alice.getScore());
    
    // Create an instance for Bob
    const bob = createStudent()
    bob.setName('Bob');
    
    // Reward Bob one time and penalize three times
    bob.rewardStudent();
    bob.penalizeStudent();
    bob.penalizeStudent();
    bob.penalizeStudent();
    
    console.log(bob.getScore());

    You could also take a class based approach to this.

    class Student {
      #privateScore = 0;
      #name = null;
    
      #changeScoreBy(points) {
        this.#privateScore += points;
      }
    
      setName(newName) {
        this.#name = newName;
      }
      rewardStudent() {
        this.#changeScoreBy(1);
      }
    
      penalizeStudent() {
        this.#changeScoreBy(-1);
      }
    
      getScore() {
        return `${this.#name}: ${this.#privateScore}`;
      }
    }
    
    const alice = new Student();
    alice.setName("Alice");
    
    //reward alice 4times
    alice.rewardStudent();
    alice.rewardStudent();
    alice.rewardStudent();
    alice.rewardStudent();
    
    console.log(alice.getScore());
    
    // Create an instance for Bob
    const bob = new Student();
    bob.setName("Bob");
    
    // Reward Bob one time and penalize three times
    bob.rewardStudent();
    bob.penalizeStudent();
    bob.penalizeStudent();
    bob.penalizeStudent();
    
    console.log(bob.getScore());
    Login or Signup to reply.
  2. The problem you created only 1 object with IIFE and assigned it to studentHogwarts. Then you use it for both students, thus actually using only 1 student. Object::create() doesn’t change anything, just returning the same object in the both cases.

    To solve the problem, remove IIFE and call the studentHorwarst() to create new students:

    const studentHogwarts = () => {
      let privateScore = 0;
      let name = null;
    
      function changeScoreBy(points) {
        privateScore += points;
      }
    
      return {
        setName: (newName) => {
          name = newName;
        },
        rewardStudent: () => {
          changeScoreBy(1);
        },
        penalizeStudent: () => {
          changeScoreBy(-1);
        },
        getScore: () => {
          return `${name}: ${privateScore}`;
        },
      };
    }
    
    const harry = studentHogwarts();
    harry.setName('Harry');
    
    //reward harry 4times
    harry.rewardStudent();
    harry.rewardStudent();
    harry.rewardStudent();
    harry.rewardStudent();
    
    console.log(harry.getScore());
    
    // Create an instance for Draco
    const draco = studentHogwarts();
    draco.setName('Draco');
    
    // Reward Draco one time and penalize three times
    draco.rewardStudent();
    draco.penalizeStudent();
    draco.penalizeStudent();
    draco.penalizeStudent();
    
    console.log(draco.getScore());
    Login or Signup to reply.
  3. Your private variables exist in the scope of what is used as a prototype object (first argument of Object.create), so both created objects share the same private variables.

    I would suggest using class syntax with private field support.

    Additionally:

    • As a student is supposed to always have a name, it seems natural to have a constructor that takes their name as argument.
    • As we know the class is about students, there is no need to put the word "student" in any of the method names
    • As with the private field syntax it is already clear from the name it is private, there is no more need to put "private" in the field name.
    class Student {
        #score = 0;
        #name = null;
    
        #changeScoreBy(points) {
            this.#score += points;
        }
    
        constructor(name) {
            this.#name = name;
        }
        reward() {
            this.#changeScoreBy(1);
        }
        penalize() {
            this.#changeScoreBy(-1);
        }
        getScore() {
            return `${this.#name}: ${this.#score}`;
        }
    }
    
    const alice = new Student('Alice');
    
    alice.reward();
    alice.reward();
    alice.reward();
    alice.reward();
    
    console.log(alice.getScore());
    
    const bob = new Student('Bob');
    
    bob.reward();
    bob.penalize();
    bob.penalize();
    bob.penalize();
    
    console.log(bob.getScore());
    Login or Signup to reply.
  4. While you can work with functions I’d argue its a good idea to do implement this the OOP way with class instances that save their individual score. That is what classes are made for.

    class StudentHogwarts {
      privateScore = 0;
      name = null;
    
      changeScoreBy(points) {
        this.privateScore += points;
      }
    
      setName(newName) {
        this.name = newName;
      }
    
      rewardStudent() {
        this.changeScoreBy(1);
      }
    
      penalizeStudent() {
        this.changeScoreBy(-1);
      }
    
      getScore() {
        return `${this.name}: ${this.privateScore}`;
      }
    }
    
    const harry = new StudentHogwarts();
    harry.setName("Harry");
    
    //reward harry 4times
    harry.rewardStudent();
    harry.rewardStudent();
    harry.rewardStudent();
    harry.rewardStudent();
    
    console.log(harry.getScore());
    
    // Create an instance for Draco
    const draco = new StudentHogwarts();
    draco.setName("Draco");
    
    // Reward Draco one time and penalize three times
    draco.rewardStudent();
    draco.penalizeStudent();
    draco.penalizeStudent();
    draco.penalizeStudent();
    
    console.log(draco.getScore());
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search