4

My understanding was that all classes were essentially functions, and all instances were essentially objects. But something confused me.

//Take this for example:

    function AnimalFunc(name) {
        this.name = name;
        this.sayName = function() {
            console.log(this.name);
        }
    }

//And compare it with this:

    class AnimalClass {
        constructor(name) {
            this.name = name;
        }
        
        sayName() {
            console.log(this.name);
        }
    }

//Now I instantiate them.

    cat = new AnimalFunc("cat")
    cat.sayName() // -> "cat"
    dog = new AnimalClass("dog")
    dog.sayName() // -> "dog"
    
    console.log(Object.keys(cat));
    console.log(Object.keys(dog));

Expected Observations:

  1. typeof(AnimalClass) and typeof(AnimalFunc) both return function.
  2. typeof(cat) and typeof(dog) both return object.

Unexpected Observations:

  1. If I do Object.keys(cat), I get ["name","sayname"].
  2. But if I do Object.keys(dog), I get ["name"].

My question is: why do I not get sayname as a key for the class instance? Why do I get it only for the function instance?

happy_sisyphus
  • 1,693
  • 1
  • 18
  • 27
  • `Object.keys` only returns *own properties*. Putting methods on `this` in the constructor instead of on the prototype is kind of just wrong. For a comparison of constructors making use of prototypes and the `class` syntax for defining them, see https://stackoverflow.com/questions/49643582/what-is-the-difference-between-prototype-based-class-syntax-and-class-syntax-in/49643668#49643668 – Ry- Apr 06 '18 at 00:33

3 Answers3

4

The first example, with a function, creates a new function property when creating an instance, and therefore, a new key for Object.keys to pick up.

The second example, with a class, assigns the function property to the object's prototype instead. Because of this, a new key isn't actually created, and the sayName function will be found by walking up the prototype chain.

You can replicate the class behavior by doing this:

function AnimalFunc(name) {
  this.name = name
}

AnimalFunc.prototype.sayName = function () {
  console.log(this.name)
}

Here's some reading if you want to familiarize yourself with JS's prototype model. Can find more articles and such by searching up "javascript prototype", if you like.

kingdaro
  • 11,528
  • 3
  • 34
  • 38
3

Because you have declared two properties named name and sayname

this.name;
this.sayname;

In the class AnimalClass you only declared as property the key name within the constructor body:

this.name = ....

Object.keys() returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

So, basically will return the properties directly declared using the context this.

Example with class

  • Declaration of the function within the constructor's body

class AnimalClass {
  constructor(name) {
    this.name = name;
    // This is just to illustrate!
    this.sayName = function() {
      console.log(this.name);
    }
  }
}

console.log(Object.keys(new AnimalClass('Dog')))
Ele
  • 33,468
  • 7
  • 37
  • 75
1

It's because AnimalClass's sayName is in AnimalClass's prototype, whereas AnimalFunc's sayName is not, it's part of the instance (this).

In the case of AnimalClass, there is only one sayName function that all instances share a reference to:

class AnimalClass {
    constructor(name) {
        this.name = name;
    }

    sayName() {
        console.log(this.name);
    }
}

var a = new AnimalClass("a"), b = new AnimalClass("b");

a.sayName === b.sayName; // true

Where as each of AnimalFunc instances has its own definition of sayName, because it is re-defined in the constructor for each instance:

function AnimalFunc(name) {
    this.name = name;
    this.sayname = function() {
        console.log(this.name);
    }
}

var a = new AnimalFunc("a"), b = new AnimalFunc("b");

a.sayName === b.sayName; // false

The eqivalent of your AnimalClass using function is:

function AnimalFunc(name) {
    this.name = name;
}

AnimalFunc.prototype.sayname = function() {
    console.log(this.name);
}

And the equivalent of your AnimalFunc using class is:

class AnimalClass {
    constructor(name) {
        this.name = name;
        this.sayName = function() {
            console.log(this.name);
        }
    }
}
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73