我最近一直在使用Javascript,并认为我已经找到原型功能,直到遇到一个小问题。
我发现如果你创建了一个对象的两个新实例,并且在这两个对象中的任何一个中你改变了它在另一个对象中改变的变量的值,这不是我当时想要的效果。必须将属性放在构造函数中,以便在每个实例中将它们分开。
何时需要让每个对象实例共享属性(字符串,bool等)?
原型是否真的只用于声明功能?并在构造函数中声明所有属性?
一个例子:
function Animal(name) {
this.name = name;
}
Animal.prototype = {
color: null,
walk: function() {}
};
var dog = new Animal('Sam');
var horse = new Animal('Bob');
dog.color = 'black';
horse.color = 'chestnut';
“狗”对象的颜色现在是“栗子”,因为该属性位于原型中并在两个实例之间共享。这种效果会在哪里?乍一看,我从中看到的是调试头痛还是在Javascript中有很好的用途?有点让我想起全球变化不好。
如果大多数时候你不想在一个实例中更改属性而在所有其他实例中也更改它,那么你只是使用原型来声明函数吗?
答案 0 :(得分:4)
在充分尊重的情况下,你误解了原型的行为
解释一切都是一个故事太长了,但是关于在原型上设置的属性,如果更改实例上的属性正在为所有实例更改此值,那么它确实是一个无限的bug来源。
看看,使用jsfiddle,jsbin,cssdeck或任何您喜欢的测试网站,在您的马/狗示例中:当您改变马的颜色时,狗的颜色不会改变。它仍然是黑色的。
为什么会这样?
当一个dog的实例对一个属性进行READ访问时,它将在原型上进行搜索,然后在原型的原型上进行搜索,依此类推,直到我们到达原型链的末尾。在找到的第一个值上,它将被返回,如果没有找到没有原型的属性,则返回undefined
因此,在实例创建之后,所有动物颜色都将为空
现在,如果您对实例的属性执行WRITE访问,则会在此实例上创建一个全新属性,并为其分配提供的值。这样原型保持其引用功能,实例获得其值。救济。
在原型上定义属性仍然很有用,因为:
1)您确保所有实例都将为这些属性提供有效值 - 默认值 -
2)您可以通过分配一个属性(更重要的是:一个大对象)来保存内存,该属性在所有实例之间共享(静态)。 Expl:狗的默认图像
3)在某些情况下,您可能希望一次更改所有实例的值。在这种情况下,更改原型上的值就可以做到这一点
4)您允许javascript解释器通过将类(在C ++经典含义中)与您的javascript类相关联来优化您的代码:当您分配原型上存在的属性时,该类不会被“破坏”,并且js解释器可以继续使用background类来表示实例。如果表现很重要,那就非常重要了。如果没有,请忘掉它: - )
所以只是一个小例子:如果你定义一个动物,那么每个实例都有它的颜色和名称:将它们设置在构造函数中是有意义的,而不是在原型上。 但是对于腿数,例如,它不会从实例更改为另一个,如果您创建子类,您可以随时更改子项上的值以使子类更改此计数:
function Animal(name, color) {
this.name = name;
this.color = null;
}
Animal.prototype.legCount = 4; // most animal have 4 legs (...)
// Dog Class, inheriting from Animal
function Dog(name) { Animal.apply(this, arguments); }
// Set Animal as Dog's prototype's prototype.
// so that we can safely change Dog's prototype.
Dog.prototype = Object.create(Animal.prototype);
// Duck class
function Duck(name) { Animal.apply(this, arguments); }
// same inheritance scheme
Duck.prototype = Object.create(Animal.prototype);
// ... but we change the legCount, for the ducks only
Duck.prototype.legCount = 2;
高级回答:
如果您希望原型属性保持不变 如果我们尝试在实例上为此属性赋值,请设置 它作为原型的只读属性。
Object.defineProperty(Duck.prototype,'legCount', { get : function() { return 2 } } );
var duck = new Duck(...) ;
duck.legCount = 5;
console.log (duck.legCount) ; // --> output is still 2
如果您想要该设置,则实例上的值会更改该值 所有实例(没有明确地改变原型的属性值),然后 在原型的属性定义的setter中完成它。
// using a closure
var duckLegCount = 2;
Object.defineProperty(Duck.prototype,'legCount', {
get : function() { return duckLegCount } ,
set : function (x) { duckLegCount = x } } );
var duck1 = new Duck(...);
var duck2 = new Duck(...);
duck1.legCount = 12;
console.log ( duck2.legCount ) ; // output is 12
答案 1 :(得分:3)
你不应该在原型中分配变量(实际上你用自定义对象覆盖Animal.prototype
)。要定义实例属性,请在构造函数中定义它们,如下所示:
function Animal(name) {
this.name = name;
this.color = null;
}
// Instance method
Animal.prototype.walk = function() {};
// Static method
Animal.run = function() {};
// Inheritance example
function Dog(name) {
// Call parent constructor
Animal.apply(this, arguments);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.walk = function() {
// Call parent method (if you need)
Animal.prototype.walk.apply(this, arguments);
...
};