hasOwnProperty seems to behave differently depending on whether it is called on a constructor function or an instance, depending on the use of this or let for the contained members.

function Animal(_name) {

    let name = _name;

    this.getName = function() {
        return name;
    }

};

function Animal2(_name) {

    this.name = _name;

    let getName = function() {
        return name;
    }

}

let a = new Animal("greg");
let a2 = new Animal2("tim");

console.log(a.hasOwnProperty("name"));
console.log(a2.hasOwnProperty("name"));
console.log(Animal.hasOwnProperty("name"));
console.log(Animal2.hasOwnProperty("name"));
console.log("");
console.log(a.hasOwnProperty("getName"));
console.log(a2.hasOwnProperty("getName"));
console.log(Animal.hasOwnProperty("getName"));
console.log(Animal2.hasOwnProperty("getName"));

This outputs the following:

false
true
true
true

true
false
false
false

Why is this the case? I understand using "let" in a constructor function emulates 'private' members, which may explain why a.hasOwnProperty("name") and a2.hasOwnProperty("getName") both return false, but don't know why the constructor functions don't 'own' their methods.

2 Answers

2
Jack Bashford On Best Solutions

Because Animal and Animal2 are constructor functions - and a function has a property name which is the name of the function. If you look at Animal.name or Animal2.name, you see that it's Animal and Animal2. And because neither Animal nor Animal2 have a property getName, only instances of Animal, the other three checks for getName return false.

function Animal(_name) {
    let name = _name;
    this.getName = function() {
        return name;
    }
};

function Animal2(_name) {
    this.name = _name;
    let getName = function() {
        return name;
    }
}
console.log(Animal.name);
console.log(Animal2.name;

0
some On

As already mentioned by others, you are getting confused since a function already have the name property. It actually have length, name, arguments, caller, and prototype.

console.log(Object.getOwnPropertyNames(function(){}));

The way you create your object is not how to do it in Javascript. For example: each instance of the object will have its own function which wastes memory and performance since it can't be optimized efficiently. (Doesn't matter if you only have two object, but soon you will have 1000s of objects and it's better to learn to do it right from the start)

function Animal(_name) {
  let name = _name;

  this.getName = function() {
    return name;
  }
};

const animal1 = new Animal('name1');
const animal2 = new Animal('name1');
console.log ( animal1.getName === animal2.getName ) // returns false

In Jacscript objects are prototype based. This is the old syntax to create define a constuctor:

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

Animal.prototype.getName =
  function() {
    return this.name;
  }

const animal1 = new Animal('name1');
const animal2 = new Animal('name1');
console.log ( 'Function', animal1.getName === animal2.getName ); // returns true
console.log ( 'Property', Animal.prototype.hasOwnProperty('getName') ); //returns true

Using hasOwnProperty on the constructor only returns true for properties defined on the constructor. In your own example getName is not defined until you run the constructor, and then the property is defined on the instance of the object, not the constructor.

In the second example it still isn't defined on the constructor, but on the prototype.

You can however put methods and values on the constructor if you would like. They are called static, since they can be accessed without an instance.

Here is an example, using the old syntax (with some new examples)

function MyOldClass() {
  // this is the construcsyntax
  console.log('old class');
}

MyOldClass.prototype.myInstanceMethod1 =
  function myInstanceMethod1() {
    console.log('instance method 1');
  }

// More efficient way to add multiple items
Object.assign(
  MyOldClass.prototype,
  {
    // anonymous function
    myInstanceMethod2: function (){
      console.log('instance method 2');
    },
    // named function (propery name and functio name can be different)
    myInstanceMethod3: function myName(){
      console.log('instance method 3');
    },
    // new shorthand syntax (both propery name and function name is the same)
    myInstanceMethod4(){
      console.log('instance method 4');
    },
    // It is posible to add values to the prototype (not possible with new syntax)
    myInstanceValue1 : 1,
    myInstanceValue2 : { prop1 : 1 }
  }
);

Object.assign(
  MyOldClass,
  {
    myStaticMethod() {
      console.log('my new static');
    },
    myStaticValue1 : 1
  }
);

console.log('Static method', MyOldClass.hasOwnProperty('myStaticMethod') ); // returns true
console.log('myInstanceMethod1', MyOldClass.prototype.hasOwnProperty('myInstanceMethod1') ); // returns true
console.log('myInstanceMethod2', MyOldClass.prototype.hasOwnProperty('myInstanceMethod2') ); // returns true
console.log('myInstanceMethod3', MyOldClass.prototype.hasOwnProperty('myInstanceMethod3') ); // returns true
console.log('myInstanceMethod4', MyOldClass.prototype.hasOwnProperty('myInstanceMethod4') ); // returns true

// Create two instances
const object1 = new MyOldClass(), object2 = new MyOldClass();

// Comparing methods on the instances. Is the same since it is comming from the prototype.
console.log( 'myInstanceMethod1', object1.myInstanceMethod1 === object2.myInstanceMethod1 );

// Comparing values on the instancees. Is the same since it is comming from the prototype.
console.log( 'myInstanceValue1 (pre change)', object1.myInstanceValue1 === object2.myInstanceValue1 );

// Changing the value on the prototype: all instances that use this prototype will have the new value
MyOldClass.prototype.myInstanceValue1 = 2;                                                                                                                              console.log( 'myInstanceValue1 changed prototype', object1.myInstanceValue1, object2.myInstanceValue1 );

// Changing the value on the instance, will create a new propery on the instance if it doesn't exist.                                                                   object1.myInstanceValue1+=3;
// Now they have different values: object1 has its own propery, while object 2 still uses the prototype.
console.log( 'myInstanceValue1 changed instance', object1.myInstanceValue1, object2.myInstanceValue1 );

// Changing on the prototype.
MyOldClass.prototype.myInstanceValue1 = 10;
// object1 still uses its own property, but object 2 have the new value since it uses the prototype
console.log( 'myInstanceValue1 changed prototype', object1.myInstanceValue1, object2.myInstanceValue1 );

// Deletes the value from object1. It will now use the prototype value.
delete object1.myInstanceValue1;
console.log( 'myInstanceValue1 after delete 1', object1.myInstanceValue1, object2.myInstanceValue1 );

// Deleting myInstanceValue1 from the instance (it if don't exists) will not delete it from the prototype
delete object1.myInstanceValue1;
console.log( 'myInstanceValue1 after delete 2', object1.myInstanceValue1, object2.myInstanceValue1 );

The same definition using the new syntax

class MyNewClass {
  constructor() {
    console.log('new class');
  }
  myInstanceMethod1(){
    console.log('instance method 1');
  }
  myInstanceMethod2(){
    console.log('instance method 2');
  }
  myInstanceMethod3(){
    console.log('instance method 3');
  }
  myInstanceMethod4(){
    console.log('instance method 4');
  }
  static myStaticMethod() {
    console.log('my new static');
  }
}

// The class syntax allows you to define methods, but if you want to add values
// your can do that the old way:
MyNewClass.prototype.myInstanceValue1 = 1;
Object.assign(
  MyNewClass.prototype,
  {
    myInstanceValue2 : { prop1 : 1 }
  }
);

Object.assign(
  MyNewClass,
  {
    myStaticValue1 : 1
  }
);

If you need to have privates, you can use WeakMap:

// Private values using WeakMap ( weakly referenced: when the instance is garbage collected,
// the private data will also be deleted )

const MyClassPrivates = new WeakMap;
class MyClass {
  constructor (name) {
    MyClassPrivates.set(this, { "name" : name });  // initializes the private data
  }
  getName() {
    const privates = MyClassPrivates.get(this); // get all private data
    return privates.name;
  }
  setName(name) {
    const privates = MyClassPrivates.get(this); // get all private data
    privates.name = name;
    return privates.name;
  }
}

const instance = new  MyClass('Elsa');
Object.freeze(instance);
console.log(instance.getName());
instance.setName('Anna');
console.log(instance.getName());