【问题标题】:Javascript __proto__ outputJavascript __proto__ 输出
【发布时间】:2014-06-02 23:05:46
【问题描述】:

以下是一些示例,展示了基于对象定义和创建方式的原型继承的不同行为。我区分对象的“原型属性”,例如someObject.prototype 和“原型引用”(我认为应该引用 someObject 继承的对象?)。

示例 1

这似乎是保留父对象的原型属性的方法。这不是推荐的继承方式吗?

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);
Parent.a = "1";
Parent.f = function() { return true; };

// add stuff to prototype property
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

console.log(Parent.__proto__)  // [Function: Object]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // { a: '1', f: [Function] }
console.log(Child.prototype)   // { b: 1, g: [Function] }
  1. 我本来希望Child.__proto__ 以同样的方式命名Parent 命名Parent.__proto__ 命名Object

  2. 我们看到Child 的原型引用指向Parent 的属性,而不是Parent.prototype 的属性。这至少对我来说是违反直觉的,因为我本来希望看到Parent.prototype 的属性bg 代替。

示例 2

混合结果。

// use a constructor instead.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// again, add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

// create differently
var Sibling = Object.create(Parent);

console.log(Parent.__proto__)   // [Function: Empty]
console.log(Parent.prototype)   // { b: 1, g: [Function] }

console.log(Child.__proto__)    // { b: 1, g: [Function] }
console.log(Child.prototype)    // undefined

console.log(Sibling.__proto__)  // [Function]
console.log(Sibling.prototype)  // { b: 1, g: [Function] }
  1. 这里,Child 的原型引用了Parents.prototype 的属性。这就是我的预期?

  2. 另一方面,Sibling 的原型引用现在是一个函数,即Parent 的原型引用。

示例 3

这似乎是保留父对象的原型引用的方法,但是您丢失了它的原型属性。

// create object constructor; add stuff.
var Parent = function () {
    this.a = "1";
    this.f = function() { return true; };
}

// add stuff to prototype property.
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

// create an object whose prototype reference is Parent (??)
var Child = function() {
    this.c = "2";
};

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

示例 1 中显示的方法是否首选,因为您可以访问父级的原型属性、它自己的属性以及 Object 的属性/方法?我在其他帖子中读到,其中一些继承方式应该是平等的……这是不正确的。另外,我读过其他帖子,例如here示例 3 是要走的路。或者我只是不确定__proto__ 代表什么......

感谢您对这些混搭情况之间的差异的任何澄清!

【问题讨论】:

  • 关于你的标题:没有__prototype__ :-)
  • 很好的问题,特别是对于低代表用户。 (票数用完了,所以不能 +1)
  • @Bergi 哈哈谢谢,我已经更正了。

标签: javascript inheritance prototype


【解决方案1】:

我区分对象的“原型成员”,例如someObject.prototype 和“原型参考”

好的术语,区分它们是理解它们的第一步and the difference between them

(我认为应该引用 someObject 继承的对象?)。

没错。与.prototype 成员相比,您可以通过Object.getPrototypeOf(obj) 或非标准的.__proto__ 属性访问原型引用。

示例 1

这不是推荐的继承方式吗?

这是一种继承方式,也是最纯粹的原型继承形式。请注意,ParentChild 是普通对象,不涉及构造函数。

// create object whose prototype reference is Object; add stuff.
var Parent = Object.create(Object);

这是第一个错误:您继承自 Object 函数(它基于构造函数模式,见下文)。你应该这样做

var Parent = {};
// or
var Parent = Object.create(Object.prototype);
// add stuff to prototype member
Parent.prototype.b = 1;
Parent.prototype.g = function() { return false; };

然而,.prototype 成员在这种继承模型中并不重要。它只是一个普通属性,在本例中是从Object 继承的。您实际上是在此处修改 Object.prototype 对象(大忌)!

// create an object whose prototype reference is Parent (??)
var Child = Object.create(Parent);

是的,Object.create 就是这样做的。

[Example 1 / 1] 我希望 Child.proto 以与 Parent.proto 命名 Object.proto 相同的方式命名 Parent。

它命名Object 只是因为那是一个命名函数。在运行时无法从对象引用中知道变量名称Parent

[示例 1 / 2] 我们看到 Child 的原型引用指向 Parent 的成员,而不是 Parent.prototype 的成员。这至少对我来说是违反直觉的,因为我本来希望看到 Parent.prototype 的成员 b 和 g。

是的,您直接继承自 Parent 对象。 .prototype 如上所述是微不足道的。 Child.prototype === Object.prototype(被继承了两次)。

示例 2 - 改用构造函数。向它的原型成员添加东西。

// create an object whose prototype reference is Parent (??)
var Child = new Parent();

[Example 2 / 1] 这里,Child 的原型引用了 Parents.prototype 的成员。这就是我上面的预期?

没有。 new operator 创建对所提供构造函数的 .prototype 成员的值的原型引用 - 在本例中为 Parent.prototype

// create differently
var Sibling = Object.create(Parent);

同样,在这里您要创建一个对象,该对象具有对函数的原型引用。不是你想要的 - 而是使用

var sibling = Object.create(Parent.prototype);
Parent.call(sibling); // optionall apply the constructor on it, like `new` does

[Example 2 / 2] 另一方面,Sibling 的原型引用现在是一个函数, 这是Parent的原型参考。

更准确地说,它是Parent 函数本身。这就是为什么您的 Sibling 确实继承了 .prototype 属性。

示例 3 - 这似乎是保留父对象原型引用的方法,但您丢失了它的原型成员:

// supposed Ad-hoc prototype inheritance
Child.prototype = Object.create(Parent.prototype)

这是为构造方法设置继承(原型引用)链所必需的。这是在 JavaScript 中实现“类”模式的标准方式。

console.log(Parent.__proto__)  // [Function: Empty]
console.log(Parent.prototype)  // { b: 1, g: [Function] }
console.log(Child.__proto__)   // [Function: Empty]
console.log(Child.prototype)   // {}

正如您在此处看到的,ParentChild 都是函数 - 确切地说是构造函数。您需要调用它们:

var childInstance = new Child();
console.log(childInstance) // {c: "2"}
console.log(childInstance.prototype) // undefined - it's not a constructor
console.log(childInstance.__proto__) // {} - the Child.prototype object
console.log(childInstance.__proto__.__proto__) // the Parent.prototype object
console.log(childInstance.__proto__.__proto__.__proto__) // the Object.prototype object

但是,您忘记了一步。 childInstance 确实从 Parent.prototype 继承了 bg,但没有在 Parent 构造函数中创建 af 属性。对于correct inheritance in the constructor model,您还必须在每个实例上应用Parent 构造函数,最好在Child 构造函数内:

function Child() {
    Parent.call(this);
    this.c = "2";
}
var childInstance = new Child();
console.log(childInstance) // {a: 1, f: [Function …], c: "2"}

【讨论】:

  • 很棒的帖子。我想我对对象和引用方面的情况有了更好的理解……但我会继续阅读!您发布的链接很有趣,并导致了许​​多其他链接。
  • 问题:在示例3中,您是否也可以在构造函数中设置Child的原型? var Child = function () { this.prototype = Object.create(Parent.prototype); Parent.call(this); }这样,一切都自给自足了?
  • 不,在构造函数内部this 是实例,而不是构造函数,您需要设置原型引用而不是某些.prototype 成员。 Setting the .prototype in the constructor is a bad ideacreating the instance in the constructor 不是 new 模式的工作方式。
【解决方案2】:

要了解Object.create 做了什么,首先您需要了解它为什么需要这样做。

JavaScript 中原型设计的经典示例使原型成为继承对象的实例:

function Parent(field) {
    this.field = field;
}

Parent.prototype.getField = function () { return this.field; }

function Child(field, field2) {
    Parent.call(this, field);
    this.field2 = field2;
}

Child.prototype = new Parent(null); // oops

Child.prototype.getField2 = function () { return this.field2; }

当我们希望它只继承方法getField 时,那条“oops”行使Child.prototype 有一个field。你可以猜到这是不卫生的(语言方面),而且不那么琐碎的例子可能会变得一团糟。

更简洁的方法是实例化一个未初始化的Parent

function inherit(parent) {
    function temp() {
    }
    temp.prototype = parent.prototype;
    return new temp();
}

Child.prototype = inherit(Parent);

技术是用相同的原型制作一个临时构造函数,这样当一个对象被创建时,构造函数不会初始化。实际上,只建立了原型链。

【讨论】:

  • 请注意,这正是 OP 在Child.prototype = Object.create(Parent.prototype) 行中所做的事情
  • @Bergi,我知道,我认为这也是 OP 困惑或缺乏知识的根源。
猜你喜欢
  • 1970-01-01
  • 2015-12-24
  • 2011-03-29
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 2011-09-07
相关资源
最近更新 更多