这让很多人感到困惑,因为 Javascript 使用了非常不同的继承和类概念。在 Javascript 中,一切,包括类,只是一个对象。构成 class 部分的所有方法等都包含在名为prototype 的对象中。其中一部分是一个名为init 的函数。当您执行new Foo() 时,您正在构造一个新对象,并且某些init 方法将包括原型在内的适当内容复制到该新对象。
所以当你通过prototype添加一个函数时
myClass.prototype.example1 = function(){};
您实际上是在向 class 添加一个新方法,然后它将在您构造的任何 myClass 实例中被继承。第二种情况,
myClass.example2 = function(){};
您只需将新方法添加到原始 myClass 对象。在 Java 术语中,这基本上是将其更改为一种新类型对象,它的行为就像一个 myClass 除了它现在有一个 example2() 方法。
更新
另一个答案提到了克罗克福德的古典继承笔记。我建议您也阅读Prototypal Inheritance 论文。
另一个更新
好的,让我们稍等一下。首先,什么是对象?基本上,它是一个结合了 state 和 behavior 的结构。
我们有一个 Number 的想法,它有一个 add 方法,我们有一种称为 Integer 的 Number,它“表现得像”一个 Number,但仅限于特定范围内的值。在像 Java 这样的语言中,您可能有
abstract class Number {
public void addTo(Number arg);
}
然后
class Integer extends Number {
int val;
public void addTo(Integer arg){ val += arg; }
}
(不要因为 Java 的细节而困扰我,我正试图说明一点。)
您在这里所说的是,可能有许多对象是数字,它们都有一个称为addTo 的行为。在数学上,由共同属性标识的事物的集合有时称为“等价类”,这就是我们得到“类”名称的方式。
我们还发现了一种特殊的 Number,称为 Integer,它的取值范围在 -32767 到 32768 之间。(测验:为什么是这些值?)不过,它在各个方面都像 Number:你也可以addTo 整数。这句话“在各方面都像——但有这些限制”通常缩写为“是一个”,这就是我们所说的“继承”。
所以现在你写
Integer a, b;
// they get initial values somehow, left as an exercise
a.addTo(b);
编译器会找出对象 a,找到它的特定行为addTo,并知道如何连接所有东西以使其工作。有时——就像在早期版本的 C++ 中——这必须在编译时完成;在后来的 C++ 和 Java 中,还有一种方法可以在运行时建立连接(“后期绑定”),这基本上归结为在某个地方放置一个表,上面写着
如果我有一个整数并且我需要一个 addTo 方法,这里是它的地址;使用它。
Javascript 和一些以前以 Self 开头的语言,在这方面做的有点不同。现在,一个对象只是一个包含……东西的结构。这些东西有些可能是数据,有些可能是函数。 “类”的概念可以完全抽象掉; “类”只是具有完全相同内容的所有对象的集合。因此,在 Javascript 中,我们可以将“Integer”类设为
var Integer = { // we're defining an object as a literal
int val,
addTo : function(b){ val += b ; }
}
(同样,如果这真的是完美的 javascript,请不要担心,重点是解释这个概念。)
如果我们复制这个名为Integer 的对象,比如Integer2,那么两者都包含一个val 和一个名为addTo 的函数。我们可以说它们都是“同一类”,因为它们具有完全相同的状态和方法。
问题是,我们如何实现这种复制?你可以一点一点地运行整个事情,但这还有一些其他问题,所以我们在 every javascript 对象的内容中定义了一个特殊的对象,名为 prototype,我们将所有方法和东西——我们想要复制的所有东西以在同一个类中构建另一个对象——在那。现在我们会有类似的东西
var Integer = {
prototype : {
int val,
addTo : function(b){ val += b; }
}
}
我们在语言中添加了一个运算符new,它基本上只是复制原型对象。当我们写
var a = new Integer();
a 现在是一个与所有其他Integer 对象具有相同原型的对象。当我们写
a.addTo(b);
解释器所要做的就是查看包含在a 中的prototype 对象并找到其名为addTo 的方法。
为什么要这样做?因为现在在类中编译、添加语法、确定何时在编译时或运行时绑定以及管理运行时表等所有复杂性都变成了两个简单的操作:
- 知道如何制作
prototype 的深拷贝
- 如何在
prototype 中按名称查找内容。
第二种方法是“原型继承”。