2

I have the following code:

class Pet {
    constructor(name) {
        this.petName = name;
    }
}

Pet.prototype.speak = {
    name: function() {
        console.log(this.petName);
    }
};

// -----------------------------------------------

const myPet = new Pet("Max");
myPet.speak.name();

I expect this code to print Max, but instead it prints undefined.

If I change the console.log to console.log(this); it prints { name: [Function: name] }. Which makes me think that function doesn't have access to the instance properties.

How can I ensure that this function has access to the instance?

Charlie Fish
  • 18,491
  • 19
  • 86
  • 179
  • 1. name refers to the function, it should be petName. 2. `this` doesn't refer to the object anymore but instead to the object the function is called in (in this case the object containing only the name() method) – Sebastian Speitel Dec 09 '18 at 19:20

2 Answers2

3

If you're targetting or have support for ES6 language features, one way to achieve what you want would be via a get method combined with an arrow function.

The get method would be declared get speak() which means it can be called without paranthesis. This method would return an object that contains a name() arrow function. Use of the arrow function here allows you to access the enclosing Pet instance via the this keyword directly:

class Pet {
    constructor(name) {
        this.petName = name;
    }
    
    // Get method allows the speak method to be called without ()
    get speak() {
      return {
        // Arrow function causes this.petName to refer to petName 
        // field of this class instance 
        name: () => {
          console.log(this.petName);
        }
      }
    }
}
 
const myPet = new Pet("Max");
myPet.speak.name();

const yourPet = new Pet("Min");
yourPet.speak.name();

Here is more information on the get method syntax and language feature.

Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
  • I think this might work. Going to try it soon. Not quite the way I was thinking of it working. But it should work I think. – Charlie Fish Dec 09 '18 at 19:30
  • @CharlieFish sure, let me know if you have any questions or need anything clarified on this – Dacre Denny Dec 09 '18 at 19:32
  • Awesome. I think this will work. One last question tho. How does this differ from using a prototype? I know prototypes tend to be better in terms of memory usage and performance a bit. Does this behave similarly to that, or does it have the same problems as the non-prototype based solutions? – Charlie Fish Dec 09 '18 at 21:22
  • @CharlieFish great to hear this has helped - please consider accepting the answer if it meets your requirements. Sorry I can't give your answer justice in this small comment area however you will find this article useful for clarification https://medium.com/javascript-scene/master-the-javascript-interview-what-s-the-difference-between-class-prototypal-inheritance-e4cd0a7562e9 – Dacre Denny Dec 09 '18 at 21:32
  • 2
    @Charlie `class` is basically just syntactic sugar for prototypes. With this answer, the `speak` getter is defined on `Pet.prototype`, but speak.name is recreated each time that getter is called. – Paul Dec 09 '18 at 21:49
1

When you call a function like this: myPet.speak.name(); then inside that function this refers to myPet.speak. In your case that is an object with one property (name) whose value is a function.

If you make speak itself be a function instead of an object, and use the property petName instead of name, it will work:

class Pet {
    constructor(name) {
        this.petName = name;
    }
}

Pet.prototype.speak = function() {
    // myPet has a `petName` property, but no `name` property
    console.log(this.petName);
};

const myPet = new Pet("Max");
myPet.speak(); // this will be `myPet` inside the function
Paul
  • 139,544
  • 27
  • 275
  • 264
  • The goal is to have `myPet.speak` be an object, and `name` to be a method of that, that logs the name. This is to make the API as easy as possible. – Charlie Fish Dec 09 '18 at 19:29
  • @CharlieFish [That's not easy](https://stackoverflow.com/questions/15884096/organize-prototype-javascript-while-perserving-object-reference-and-inheritance). Better just call the method `speakName`. – Bergi Dec 09 '18 at 21:05
  • @Pual but if I use `arrow function` like this `Pet.prototype.speak = () => this.petName` this will not work – Azhar Uddin Sheikh Mar 19 '22 at 07:21