xcode xcode - 2 months ago 9
Javascript Question

.isPrototypeOf() and .hasOwnProperty() method confusion

Let say i have this code:



// Male will inherit ALL of the Human properties
function Human(x, y) {
// Following properties will be inherited
this.name = x;
this.age = y;
this.test = "Test 1";
}
// Following properties will ALSO be inherited
Human.prototype.citizen = "USA";
Human.prototype.employer = "Google";
Human.prototype.test = "Test 2";

function Male(x, y) {
// Following properties will be the own properties of Male instances
this.name = x;
this.age = y;
this.gender = "Male";
}

// Inheritance - Connecting Male object with Human object
Male.prototype = new Human(); // no arguments are passed
Male.prototype.constructor = Male; // correcting constructor property

var albert = new Male("Albert", 25);





Then, i want to do some testing on the code

Human.isPrototypeOf(albert); // I expect it to return TRUE


But it return FALSE, why is that?

and, for the following test

Human.hasOwnProperty("age"); // /i expect it to return TRUE


But it return FALSE, why is that?

Thanks,

Edit
My questions are slightly different with the other question since it's also talk about prototype chain.

Answer

The Human function isn't in albert's prototype chain. The object referenced by Human.prototype is:

Human.prototype.isPrototypeOf(albert); // true

Human.hasOwnProperty("age"); // /i expect it to return TRUE

The Human function doesn't have an age property. Instances created with it by new do:

new Human().hasOwnProperty("age"); // true

Side note: The way you're setting up the inheritance chain is common and shown in a lot of examples, but incorrect in two ways:

  1. You don't want to use new Human to create Male.prototype.

  2. You do want to call Human from Male:

So:

function Male(x, y) {
    // Give Human its chance to initialize the object (#2)
    Human.call(this, x, y);
    // ...
}

// Don't use new Human to create the prototype (#1)
Male.prototype = Object.create(Human.prototype);
Male.prototype.constructor = Male;

The reason you don't use new Human to create the prototype for Male is simple: Human expects arguments, but you don't have any to give it.

Here's an updated ES5 and earlier version of that code:

function Human(name, age) { // Argument names should be meaningful
  this.name = name;
  this.age = age;
  this.test = "Test 1";
}

Human.prototype.citizen = "USA";
Human.prototype.employer = "Google";
Human.prototype.test = "Test 2";

function Male(name, age) {
  Human.call(this, name, age);
  this.gender = "Male";
}

Male.prototype = Object.create(Human.prototype);
Male.prototype.constructor = Male;

var albert = new Male("Albert", 25);
console.log(Human.prototype.isPrototypeOf(albert)); // true
console.log(new Human().hasOwnProperty("age"));     // true

Or of course, use ES2015+ (transpiling if your target doesn't support it yet):

// THIS SNIPPET REQUIRES A BROWSER WITH ES2015+ SUPPORT

class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.test = "Test 1";
  }
}

Human.prototype.citizen = "USA";
Human.prototype.employer = "Google";
Human.prototype.test = "Test 2";

class Male extends Human {
  constructor(name, age) {
    super(name, age);
    this.gender = "Male";
  }
}

let albert = new Male("Albert", 25);
console.log(Human.prototype.isPrototypeOf(albert)); // true
console.log(new Human().hasOwnProperty("age"));     // true


You've said you're trying to see how the chain works. Here's a diagram of what we have in memory after creating albert (with several details removed for simplicity):

         +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
         |                                                                      |
         \ +−−−−−−−−−−−−−−−−+                                                   |
Human−−−−−>|    function    |                                                   |
           +−−−−−−−−−−−−−−−−+                            +−−−−−−−−−−−−−−−−−−−−+ |
           | prototype      |−−−−−−−−−−−−−−−−−−−−−−−−−−−>|       object       | |    
           | name: "Human"  |                          / +−−−−−−−−−−−−−−−−−−−−+ |    
           +−−−−−−−−−−−−−−−−+                          | | constructor        |−+
                                                       | | citizen: "USA"     |
         +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | | employer: "Google" |
         |                                           | | | test: "Test 2"     |
         \ +−−−−−−−−−−−−−−−−+                        | | +−−−−−−−−−−−−−−−−−−−−+
Male−−−−−−>|    function    |                        | |
           +−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−+ | |
           | prototype      |−−−>|     object      | | |
           | name: "Male"   |  / +−−−−−−−−−−−−−−−−−+ | |
           +−−−−−−−−−−−−−−−−+  | | constructor     |−+ |
                               | | [[Prototype]]   |−−−+
           +−−−−−−−−−−−−−−−−+  | +−−−−−−−−−−−−−−−−−+
albert−−−−>|     object     |  |
           +−−−−−−−−−−−−−−−−+  |
           | name: "Albert" |  |
           | age: 25        |  |
           | gender: "Male" |  |
           | [[Prototype]]  |−−+
           +−−−−−−−−−−−−−−−−+ 

[[Prototype]] above is the name the spec uses for the "internal slot" of an object that contains its reference to its prototype object. In contrast, prototype, the property on functions (e.g., Human.prototype), is just a normal property of functions that points to the object that new will use as the [[Prototype]] of the new object it creates if you use that function with new.

Some notes on that diagram:

  • All functions have a [[Prototype]] internal slot that points to the object Function.prototype points to (omitted above for simplicity).
  • Human, the function, has a name property: "Human"
  • Male, the function, has a name property: "Male"
  • The object albert refers to has a name property: "Albert"
  • albert's [[Prototype]] is Male.prototype; Male.prototype's [[Prototype]] is Human.prototype (and Human.prototype's [[Prototype]], not shown, is `Object.prototype).

In a comment you've said:

I just can't understand why after inheritance statement we can do Male.prototype.isPrototypeOf(albert) return true but not in Human.isPrototypeOf(albert) (it return false) since Male.prototype is an instance of Human

Because Human, the function, is nowhere in albert's prototype chain. Lets' look at albert's prototype chain:

  • albert's [[Prototype]] is Male.prototype
  • Male.prototype's [[Prototype]] is Human.prototype
  • Human.prototype's [[Prototype]] is Object.prototype
  • Object.prototype's [[Prototype]] is null

As a diagram:

+−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−−−−−+ 
|    albert     |    | Male.prototype |    | Human.prototype |    |  Object.prototype   |
+−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−−−−−+ 
| [[Prototype]] |−−−>| [[Prototype]]  |−−−>| [[Prototype]]   |−−−>| [[Prototype]]: null |
+−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−−−−−−−+ 

So Human, the function, is nowhere in that chain.