.isPrototypeOf() 和 .hasOwnProperty() 方法混淆

.isPrototypeOf() and .hasOwnProperty() method confusion

假设我有这个代码:

// 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);

然后,我想对代码做一些测试

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

但它 return 错误,这是为什么?

并且,对于下面的测试

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

但它 return 错误,这是为什么?

谢谢,

编辑 我的问题与另一个问题略有不同,因为它也是在谈论原型链。

Human 函数 不在 albert 的原型链中。 Human.prototype引用的对象是:

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

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

Human 函数没有 age 属性。 new 使用它创建的实例执行:

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

旁注:您设置继承链的方式很常见,在很多示例中都有展示,但有两个地方不正确:

  1. 您不想使用 new Human 创建 Male.prototype

  2. 想从Male打电话给Human:

所以:

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;

您不使用 new HumanMale 创建原型的原因很简单:Human 需要参数,但您没有任何参数。

这是该代码的更新版 ES5 和早期版本:

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

当然,也可以使用 ES2015+(如果您的目标还不支持,请转译):

// 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


您说过您正在尝试查看链条的工作原理。这是创建 albert 后我们在内存中的图表(为简单起见删除了一些细节):

         +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
         |                                                                      |
         \ +−−−−−−−−−−−−−−−−+                                                   |
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]] 是规范用于对象 "internal slot" 的名称,该对象包含对其原型对象的引用。相比之下,prototype,属性 函数(例如,Human.prototype),只是一个普通的 属性 函数,指向 new 将要执行的对象如果您将该函数与 new.

一起使用,则用作它创建的新对象的 [[Prototype]]

关于该图的一些注释:

  • 所有函数都有一个[[Prototype]]内部插槽指向对象Function.prototype指向(为简单起见上面省略)。
  • Human, 函数, 有一个 name 属性: "Human"
  • Male, 函数, 有一个 name 属性: "Male"
  • 对象albert指的是name属性:"Albert"
  • albert[[Prototype]]Male.prototypeMale.prototype[[Prototype]]Human.prototypeHuman.prototype[[Prototype]],未显示,是 `Object.prototype)。

您在评论中说:

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

因为Human这个函数在albert's prototype chain. Lets'中不存在看albert的原型链:

  • albert[[Prototype]]Male.prototype
  • Male.prototype[[Prototype]]Human.prototype
  • Human.prototype[[Prototype]]Object.prototype
  • Object.prototype[[Prototype]]null

如图:

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

所以 Human 函数不在该链中。