有人可以告诉我为什么结果是什么,而不是我期望的结果。这让我疯了!
var f = function(b){
console.log(this.config);
this.config.b = b;
}
f.prototype.config = {
a: 'a',
b: 'b'
};
var f1 = new f('bb');
var f2 = new f('bbb');
// logs
// { a: 'a', b: 'b' }
// { a: 'a', b: 'bb' }
// expected
// { a: 'a', b: 'b' }
// { a: 'a', b: 'b' }
答案 0 :(得分:9)
这不是被修改的原型,而是你在原型上放置的config
对象。这是正确的行为,创建新实例时不会复制原型引用的对象。 f1.config === f2.config
,他们指向相同的对象。
原型链适用于get
操作的方式是:
this.config
。config
,因此我们继续下一步。undefined
。(set
操作的工作方式不同; set
操作总是更新或创建其所设置对象的属性,从不进一步向下[原型?]原型链。)
因此,在您的情况下,由于您的实例没有config
属性,我们将转到原型。由于原型 具有config
属性,因此使用它。该属性的值是一个对象引用,因此如果您更改对象(通过分配其属性之一),它将被更改,并且其他任何使用该对象的内容都将看到更改。 / p>
另一种看待它的方法是做一个图表:
+------+ +------+ | f1 | | f2 | +------+ +------+ | | +------+-------+ | v +--------------------+ +--------+ | [[Proto]] assigned | | config | | via `new f` |------>| object | +--------------------+ +--------+ | +-------+-------+ | | V v +------------+ +------------+ | a property | | b property | +------------+ +------------+
另一种的想法是让函数和原型完全不受影响:
var pseudoProto = {}; // A stand-in for the prototype...
pseudoProto.config = { // ...with `config` on it
a: 'a',
b: 'b'
};
var f1 = {}; // A blank object...
f1.pseudo = pseudoProto; // ...referencing `pseudoProto`
var f2 = {}; // Another blank object...
f2.pseudo = pseudoProto; // ...also referencing `pseudoProto`
f1.pseudo.config.b = "bb"; // Change the `b` property on `config`
console.log(f2.pseudo.config.b); // Logs "bb", of course
以一种非常真实的方式,这就是通过new f()
在幕后发生的事情。您无法直接访问指向原型的f1
和f2
实例的属性(规范称之为[[Proto]]
属性),但这是真实的,它确实是那里。 [仅供参考:ECMAScript规范的latest version允许我们直接使用[[Proto]]
属性执行一些操作,例如创建具有特定[[Proto]]
的原始对象(不通过函数),但是仍然没有让我们直接访问该物业本身。]
有很多时候你希望所有实例共享同一个对象(例如,函数对象!),因此原型是这些对象引用的正确位置;但是如果您希望每个实例都拥有该对象的拥有副本,则需要在构造函数中创建该对象。在你的情况下:
var f = function(b){
this.config = {
a: 'a',
b: b
};
}
var f1 = new f('bb');
console.log(f1.config);
var f2 = new f('bbb');
console.log(f2.config);
// Logs // { a: 'a', b: 'bb' } // { a: 'a', b: 'bbb' }
(注意我移动了console.log
语句,所以我们看到结果而不是中间状态。)
答案 1 :(得分:0)
这是一个很好的coffeescript示例,展示了这个问题:
Person = class
config:
name: "sin nombre"
constructor: (config={}) ->
@config.name = config.name if config.name?
getName: -> @config.name
Human = class extends Person
ben = new Human(name: 'Ben')
sonny = new Human()
alert ben.getName()
alert sonny.getName()
和解决方案:
Person = class
config:
name: "sin nombre"
constructor: (config={}) ->
@config = {}
@config[key] = value for own key, value of Person::config
@config.name = config.name if config.name?
getName: -> @config.name
Human = class extends Person
ben = new Human(name: 'Ben')
sonny = new Human()
alert ben.getName()
alert sonny.getName()