4

在原型语言中,对象基本上可以相互克隆。

所以,假设我们有一个构造函数:

Bla = function()
{
    this.a = 1;
}

我可以像这样创建该对象的新实例x = new Bla();:现在,x.a返回 1。

如果我要写Bla.prototype.b = 2,那么x.b将返回 2。但是,为什么?如果 x “克隆” Bla,为什么我不能只说Bla.b = 2,而不引用Bla.prototype,并且仍然获得相同的功能?这与this关键字有关吗?

4

2 回答 2

10

ECMAScript (JavaScript) 支持“基于原型的继承”。这意味着 JS 中的“类”和“实例”之间没有区别。相对于其他语言的 OOP,在 JS 中“类”和“实例”基本上是一回事:

当您定义Bla时,它会立即实例化(准备好使用),但也可以用作“原型”来克隆object Bla具有相同属性和方法的另一个实例的初始(!)定义。

在 ECMAScript 中,一切都是对象,即使是对象的初始定义。在其他 OOP 语言中,定义部分有一个“类”。

prototype对象适用于您想要在初始定义之后扩展“Bla”的原型(阅读:类“Bla”)并将新属性/函数添加到所有当前和未来实例的情况Bla

如果您现在感到困惑,我认为此代码示例可能有助于发现差异:

// when defining "Bla", the initial definition is copied into the "prototype"
var Bla = function()
{
    this.a = 1;
}
// we can either modify the "prototype" of "Bla"
Bla.prototype.b = 2;
// or we can modify the instance of "Bla"
Bla.c = 3;

// now lets experiment with this..

var x = new Bla();  // read: "clone the prototype of 'Bla' into variable 'x'"  
console.log(x.b);     // logs "2"  -- "b" was added to the prototype, available to all instances

console.log(x.c);     // undefined   -- "c" only exists in the instance "Bla"
console.log(Bla.c);   // logs "3"  -- "Bla" is an object, just like our new instance 'x'

// also note this:
Bla.a = 1337;
var y = new Bla();
console.log(y.a);     // logs "1"  -- because the "prototype" was cloned, 
                // opposed to the current state of object "Bla"
console.log(Bla.a);   // logs "1337" 

正如您在最后一个示例中看到的,“原型”的概念对于避免克隆对象的当前“状态”是必要的。

如果不以这种方式实现,您可能会得到奇怪的效果,因为如果在克隆之前使用/修改了原始对象“Bla”,那么当前状态也会被复制。这就是语言设计者选择prototype构造的原因。

永远记住:“Bla”不是静态定义,就像“类”在其他 OOP 语言中一样。


ECMAScriptprototype规范说:

原型是用于在 ECMAScript 中实现结构、状态和行为继承的对象。当构造函数创建一个对象时,该对象隐式引用构造函数的关联原型,以解析属性引用。构造函数的关联原型可以由程序表达式constructor.prototype引用,并且添加到对象原型的属性通过继承由共享原型的所有对象共享。

一些 JS 框架,例如同名的“原型”,大量使用此功能来扩展 Javascript 内置对象的功能。

例如,它array使用方法扩展原生对象forEach,将这个缺失的特性添加到 JS:

Array.prototype.forEach = function each(iterator, context) {
  for (var i = 0, length = this.length >>> 0; i < length; i++) {
    if (i in this) iterator.call(context, this[i], i, this);
  }
}
于 2012-06-16T19:24:26.717 回答
4

它与类和实例之间的区别有关。x 是 Bla 类的一个实例。如果设置 xb = 2,则为该实例添加/设置属性 b。如果在 b 的原型上设置 b,则为该类(以及每个现有实例)创建一个属性。同时这也是在构造函数中设置它的区别,就像在此处使用 a 所做的那样:当在实例上更改 a 时,它......改变得很好。如果 b 改变了,原型 b 仍然存在,它的旧值,但是一个新的属性 b 为那个实例创建了新的值。(这对于重置为默认值很有用)

编辑意识到文本有时比代码示例清晰得多,所以添加了一个完整的示例,我希望能说明差异

​function bla(){
    this.a= 1;
}

var x = new bla(),
    y= new bla();


bla.prototype.b= 2;
x.c = 3;

//x already exists, but b is still set
//by setting the prototype all existing instances are changed
console.log(x.b); //2
console.log(y.b); //2

//because c has been explicitly set, it only exists on instance x
console.log(x.c); //3
console.log(y.c); //undefined


x.b = 6; //at this point
//as mentioned, setting a property on an instance, only affects
//that instance
console.log(x.b); //6
console.log(y.b); //2

//but, because b is also a prototype property, it can be 'reset'
//by deleting the instance property
delete x.b; //removes the instance b
delete y.b; //on y this does nothing, because only the prototype property exists
console.log(x.b); //2 (reset to prototype)
console.log(y.b); //2

//the final difference, because a is a class bla property, and
//not of its prototype, deleting property 'a', will actually cause a remove
delete x.a;
console.log(x.a); //undefined
于 2012-06-16T19:22:01.347 回答