3

(NOTE TO HELPER: this question might not have a solution)

Greetings all,

I was solving a little challenge I gave myself. When I encounter a seemly impossible task. The challenge goes as follow.

TOP LEVEL
Have a function which uses closures to simulate a class. (by class I mean somewhat what you get in C++ or C# when you declare a new class.) The top function will return an inner function which has props and that returns methods to change those props. This means that I cannot access the props directly. (what you get when you declare variables under private in traditional classes)

CODE

const Person = function() { //outer function
  const closures = () => { // inner function 
    let firstName, lastname, age, gender; // "class" properties (props) unknown to Person

    return { // returns the methods I can use to update or get the props + functionalities

      //setters - can set the properties of closures
      setFirstName: (name) => {
        firstName = name;
      },
      setLastName: (surname) => {
        lastname = surname;
      },
      setAge: (newAge) => {
        newAge > 0 && newAge < 150 ? age = newAge : console.warn(`Age has not been set, ${newAge} is out of range`);
      },
      setGender: (newGender) => {
        const personExist = (firstName || lastname);
        personExist? gender = newGender : console.warn("This person does not exist, you need to define their first or last name . . .");

        personExist?checkGender(newGender) ? gender = newGender : console.warn(`Gender has not been set. ${newGender} is not supported at the moment.`) : null;
        ;
      },

      //getters - can get the properites of closures
      getFirstName: () => firstName,
      getLastName: () => lastname,
      getAge: ()=> age,
      getGender: ()=>gender,

      //Functionalities -- things that a Person obj can do
      introduction: () => {
        let p1 = firstName ? `Grettings, my name is ${firstName}` : lastname ? `Last Name is ${lastname}` : '';
        let p2 = firstName ? 
                  lastname ? ` ${lastname}.` : '' :
                  lastname ? `. You can refer to me as ${lastname}`: '';
        let p3 = age? `I am ${age} years-old.` : '';

        let p4 = p1 != '' || p2 != '' ? `${lastname?`${p1}${p2} ${p3}`: `${p1}.${p2} ${p3}`}` : "I do not know me . . .";

        return `${p4}`
      },

      speak: function to(Person) { // troublesome part

        if(typeof Person == 'object'){
          console.log(`> ${Person.introduction()}`); 
          // console.log(name.caller)
          // console.log(name.prototype)
          // everything was going well till this part of the challenge, where I am trying to get name.caller so i can access its props
        } else {
          console.warn(`Cannot talk to ${Person} because ${Person} is not of type person.`)
        }
      }
    }
  }

  return closures();
}


// this is the entire code, feel free to use it in anyway whatsoever. 

PROBLEM
The last part of the challenge where I am struggling on is when I am trying to implement the "speak" function.

-- Expected behavior
A Person instance (not the first ever created) will call speak, passing-in another person instance. Within speak, the argument will introduce itself using one of the methods declared. Then the caller will introduce itself.

--Troublesome Part
I cannot access "to's" (the function sub-name for speak) caller. (in case your wondering, I need to give it a sub-name because if I try to call "speak" it will yield an error.) The easy solution is to also pass-in the caller as an argument, but I want to be able to access it using the caller method instead.

for example the following code


let rian = Person(); 
rian.setFirstName("Rian");
rian.setLastName("Arias");
rian.setGender("Male");
rian.setAge(30);


let pepe = Person();
pepe.setFirstName("Pepe");
pepe.setLastName("Lopez"); 
pepe.setGender("Male");

pepe.speak(dario); // will not work because I haven't been able to get 'caller' to work 

Should output the following

> Grettings, my name is Dario Arias. I am 20 years-old. < Grettings, my name is Pepe Lopez.

ranieribt
  • 1,280
  • 1
  • 15
  • 33
Dario
  • 228
  • 1
  • 9
  • 2
    I suppose you meant `pepe.speak(rian);` as the last line? What is `checkGender`? – trincot Dec 30 '19 at 23:09
  • If you want to be able to be *sure* that the parameter passed to `speak` is another `Person`, you'll need another closure as well. – CertainPerformance Dec 30 '19 at 23:16
  • I just want to share your code in a [repl](https://repl.it/@alexanderwolf/Person-class-example). – AWolf Dec 30 '19 at 23:31
  • checkGender is a method that takes in an input and returns true if such input is a valid gender, else returns false. I did mean to use Rian, I was in a rush when I wrote this which is why the entire explanation lacks. – Dario Dec 31 '19 at 06:04

2 Answers2

2

You can have access to the current person instance with this:

console.log(`> ${this.introduction()}`); 

function checkGender(gender) { return ["Male", "Female"].includes(gender) }

const Person = function() { //outer function
  const closures = () => { // inner function 
    let firstName, lastname, age, gender; // "class" properties (props) unknown to Person
    

    return { // returns the methods I can use to update or get the props + functionalities

      //setters - can set the properties of closures
      setFirstName: (name) => {
        firstName = name;
      },
      setLastName: (surname) => {
        lastname = surname;
      },
      setAge: (newAge) => {
        newAge > 0 && newAge < 150 ? age = newAge : console.warn(`Age has not been set, ${newAge} is out of range`);
      },
      setGender: (newGender) => {
        const personExist = (firstName || lastname);
        personExist? gender = newGender : console.warn("This person does not exist, you need to define their first or last name . . .");

        personExist?checkGender(newGender) ? gender = newGender : console.warn(`Gender has not been set. ${newGender} is not supported at the moment.`) : null;
        ;
      },

      //getters - can get the properites of closures
      getFirstName: () => firstName,
      getLastName: () => lastname,
      getAge: ()=> age,
      getGender: ()=>gender,

      //Functionalities -- things that a Person obj can do
      introduction: () => {
        let p1 = firstName ? `Grettings, my name is ${firstName}` : lastname ? `Last Name is ${lastname}` : '';
        let p2 = firstName ? 
                  lastname ? ` ${lastname}.` : '' :
                  lastname ? `. You can refer to me as ${lastname}`: '';
        let p3 = age? `I am ${age} years-old.` : '';

        let p4 = p1 != '' || p2 != '' ? `${lastname?`${p1}${p2} ${p3}`: `${p1}.${p2} ${p3}`}` : "I do not know me . . .";

        return `${p4}`
      },

      speak: function to(Person) { // troublesome part

        if(typeof Person == 'object'){
          console.log(`> ${Person.introduction()}`); 
          console.log(`> ${this.introduction()}`); 
          // console.log(name.caller)
          // console.log(name.prototype)
          // everything was going well till this part of the challenge, where I am trying to get name.caller so i can access its props
        } else {
          console.warn(`Cannot talk to ${Person} because ${Person} is not of type person.`)
        }
      }
    }
  }
  return closures();
}

let rian = Person(); 
rian.setFirstName("Rian");
rian.setLastName("Arias");
rian.setGender("Male");
rian.setAge(30);


let pepe = Person();
pepe.setFirstName("Pepe");
pepe.setLastName("Lopez"); 
pepe.setGender("Male");

pepe.speak(rian);
trincot
  • 317,000
  • 35
  • 244
  • 286
  • This has nothing to do with Repl.it. If you get the window object, it means you did not call the function with dot-notation, like `pepe.speak()`, but probably passed the function reference around, like for instance, `setTimeout(pepe.speak)`, in which case the function will not be called with `pepe` as `this`. See for extensive explanation [how does the this keyword work](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – trincot Dec 31 '19 at 09:31
0

So I believe I have found your error. As a good rule of thumb in js, it is best to have only 1 variable per name. This might have just slipped through your eyes, I do this all the time, but you have two variables named Person. I changed your speak function to this:

speak: function (p) { // troublesome part
    if(typeof p == 'object'){
      console.log(`> ${p.introduction()}`);
    } else {
      console.warn(`Cannot talk to ${p} because ${p} is not of type person.`)
    }
  }

And it worked like a charm. I also removed the reference to "checkGender" and I changed "dario" to "rian" and it outputs what I believe you would want. Also, I removed the "to" subname for the function as I am unsure of why you would want/need a subname.

oriont
  • 684
  • 2
  • 10
  • 25