【问题标题】:JavaScript Prototypes - ConfusedJavaScript 原型 - 困惑
【发布时间】:2016-04-26 21:05:26
【问题描述】:

我现在正在研究原型,并认为我快到了,但我对一个主题有点困惑。

假设我们有:

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

function Cat(species) {
  this.species = species;
}

Cat.prototype.color = null;

Cat.prototype = new Animal();

我的问题是,为什么新属性需要prototype

为什么我们不能这样做:

Cat.color = null;

编辑::

所有的白色块都来自另一个 SO 帖子的 uml 图。我添加了橙色框以适应我提供的这个示例。我添加的这张图还有意义吗?

我认为我的主要问题是我让函数构造函数和实际原型对象过于相似,而实际上它们是完全不同的东西。一个是函数,一个是对象。

编辑 2

通过此图,我试图阐明constructor 属性如何交互以及它与什么确切连接,更具体地说,它如何影响this 的使用。任何关于有效性的 cmets 都会有所帮助。

【问题讨论】:

  • “为什么新属性需要prototype 这是一个关于为什么 Brendan Eich 决定以这种方式设计语言的问题(只能回答由他提出),或者这纯粹是一个技术问题(在“CatCat.prototype 之间有什么区别”的意义上)?
  • 顺便说一下,如果你想让Cat成为Animal的扩展,正确的做法是:Cat.prototype = Object.create(Animal.prototype); Animal.prototype.constructor = Animal;
  • @vtange 您刚刚解释的内容与仅使用Cat.prototype = new Animal(): 相比有什么好处:Cat 原型是Animal 的一个实例。 (我想我说的没错)
  • 如果您按照自己的方式进行操作,您的意思是“所有 Cats 都应该与(创建一个没有输入的新 Animal)相同。”这意味着猫与没有名称和性别的动物相同。更多信息请访问stackoverflow.com/questions/16100471/…
  • @vtange 我阅读了 SO 帖子并理解您为什么现在建议这样做,但您为什么要使用 Animal.prototype.constructor = Animal。根据我的阅读,我认为您需要改为对Cat 执行此操作:Cat.prototype.constructor = Cat。这样,this 上下文将引用 Cat 构造函数,而不是 Animal 构造函数。这是正确的吗?

标签: javascript oop properties prototype


【解决方案1】:

好问题:

Cat.color = null;只在那个Cat 上设置颜色,如果你把它放在prototype 上,任何'Cat' 之后实例化也将包含color 属性。

假设您有类似var tabby = new Cat('feline') 的代码,上面的代码没有prototype tabby 不会有color

【讨论】:

  • 准确地说,Cat.color = null;Cat 函数对象上设置了color 属性。不在 Cat 的实例上,也不在 Cat 原型上。
  • @JordanHendrix,谢谢。我想我感兴趣的是为什么 JavaScript 被设计为需要原型。为什么实际的Cat函数对象不能作为继承和实例化的基础,而不是一些抽象的prototype概念?
  • @inthenameofmusik:这意味着Cat 的每个实例都是一个函数,因为Cat 是一个函数。
  • 我明白了,这对人们来说有点傻,但它就是 js 的方式:javascriptissexy.com/…
  • @inthenameofmusik: "而不是一些抽象的 *prototype 概念"* 请注意,JavaScript 并不是唯一使用原型的语言,尽管其他大多数语言并不流行. en.wikipedia.org/wiki/Prototype-based_programming
【解决方案2】:

“Javascript 中的一切都是对象”

有没有想过为什么不用Number.toString = function(){....?就可以在数字50上使用.toString()

因为它是内置在 Javascript 中的。所有 Number.prototypes 都有.toString 方法。数组、对象、字符串等不胜枚举。

每次在 JS 中编写 Number 时,想象(实际上没有调用构造函数)调用 Number 构造函数,类似于 Animal 和 Cat 的构造函数。

这就是构造函数的作用。他们创建了一个(他们的名字).prototype 的实例。这就是为什么function Animal() 制造了一些Animal.prototype 等等。

function Animal()function Cat() 否则与 Animal.prototypeCat.prototype 无关。如果你真的用new Animal()创建了一个新的Animal然后改变了构造函数,你刚刚创建的新Animal不会被更新,因为它是在构造函数改变之前构造的。

假设你制作了一个Cat“霍布斯”

然后,当你说Cat.prototype.color = null; 时,你是说 Cat.prototype 的所有对象都应该有一个“null”的颜色值。这将更新您之前构建的 Cats,因为现在当您尝试查找 Hobbe 的颜色时,它会吐出 undefined,因为您没有在 function Cat() 中为 Hobbes 本人指定颜色,但随后 JS 将回溯到 Hobbe 的 Cat.prototype发现颜色其实是null

希望对您有所帮助。

【讨论】:

  • FWIW,原始值不是对象。没有调用构造函数。只有当原始值被like对象访问(属性访问)时,它才会临时转换为对象。
  • 是的,正确的。这只是我为了帮助 OP 理解我们为什么使用原型而做的一个类比。
  • @FelixKling 你介意看看我用图表所做的编辑,让我知道我的图表方式是否有意义吗?
  • @vtange 如果您不介意评论我在顶部上传的图表的正确性。谢谢。
【解决方案3】:

需要提醒的几件事;

  1. 构造函数用于实例化新对象。如果构造函数具有使用前面的 this. 关键字定义的变量,那么这些将是实例化对象的属性。使用前面的 var 关键字定义的变量不会成为实例化对象的一部分。
  2. 由构造函数实例化的对象会将其原型分配给构造函数的原型,因此它们可以直接访问构造函数的原型。这意味着他们可以使用(共享)构造函数原型中的属性和函数,就好像它们是他们自己的一样。
  3. 当您考虑从构造函数实例化数千个对象时,原型非常有用。每个对象的唯一属性,例如名称、id、颜色或任何应该在构造函数中使用前面的this. 定义的东西。例如this.namethis.color,但是它们应该共享的功能应该在构造函数原型中定义,因为在每个实例化对象中为它们保留空间显然会浪费内存。

【讨论】:

  • " 如果构造函数的变量定义在前面的this." 从技术上讲,这些都不是变量。
  • @Redu 谢谢,这真的帮助我更接近理解。所以...需要一个函数构造函数一个原型而不是仅仅有一个函数构造函数的原因是可以在链中引用非特定属性,从而节省内存而不是将它们传递到每个实例。这是在 JavaScript 中使用此构造函数/原型设计的唯一原因,还是我错过了更多原因?我想知道所有这些复杂性的权衡是否值得性能提升。
  • @inthenameofmusik:“所有这些复杂性的权衡” 我认为原型并不比变量范围复杂多少,它基本上以非常相似的方式工作。原型的一个非常实用的优势是 polyfill:您可以通过扩展各自的原型(例如 Array.prototype)轻松添加对新 API 的支持,而不会对数组的用户/创建者产生任何影响。
  • @inthenameofmusik 你说得对……就是这样。想象一下所有驻留在 Array.prototype 或 Object.prototype 中的数组和对象函数,它们可以从代码中实例化的所有对象和数组直接访问。还有什么比这更好的?
  • @Redu 谢谢。我现在明白了。您是否介意评论我上面发布的图表,它是否正确表示正在发生的事情。我相信我应该让animal.prototype 指向一个新的object.prototype 框,但我不确定object.prototype 框应该指向什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
相关资源
最近更新 更多