Javascript中的原型

时间:2012-06-16 19:17:57

标签: javascript oop prototype prototypal-inheritance

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

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

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关键字?

2 个答案:

答案 0 :(得分:10)

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

当您定义“Bla”时,它会立即实例化(准备使用),但也可以作为“原型”来克隆具有相同属性的另一个实例的对象“Bla”的初始定义(!)和方法。在其他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'"  
alert(x.b);     // alerts "2"  -- "b" was added to the prototype, available to all instances

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

// also note this:
Bla.a = 1337;
var y = new Bla();
alert(y.a);     // alerts "1"  -- because the initial definition was cloned, 
                // opposed to the current state of object "Bla"
alert(Bla.a);   // alerts "1337" 

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

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

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


ECMAScript specificationprototype

  

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

所有JS框架(如同名“prototype”或jQuery都大量使用此功能来扩展Javascripts内置对象的功能。

例如,JS框架“prototype”使用方法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);
  }
}

答案 1 :(得分:4)

它与类和实例之间的区别有关。 x是类Bla的一个实例。如果设置x.b = 2,则为该实例添加/设置属性b。 如果在b的原型上设置b,则为该类(以及每个现有实例)创建一个属性。 同时它是在构造函数中设置它与在此处完成之间的区别:当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