在原型语言中,对象基本上可以互相克隆。
所以,假设我们有一个构造函数:
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
关键字?
答案 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 specification说prototype
:
原型是用于在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