我每周都参加这次聚会,讨论有效的javascript:68种方式。
在第36项:仅在实例对象上存储实例状态,我们创建了以下示例来解释它。
function User() {}
User.prototype = {
hobbies: [], // should be instance state!
addHobby: function (x) {
this.hobbies.push(x);
}
};
我们实例化以下用户。
boy = new User();
// User {hobbies: Array[0], addHobby: function}
girl = new User();
// User {hobbies: Array[0], addHobby: function}
boy.addHobby("swimming");
girl.addHobby("running");
// undefined
boy.hobbies
// ["swimming", "running"]
girl.hobbies
// ["swimming", "running"]
如您所见,addHobby功能会影响原型级别的爱好。
现在,如果我将整个代码更改为
function User() {}
User.prototype = {
hobbies: [], // should be instance state!
addHobby: function (x) {
newArr = new Array(x);
this.hobbies = this.hobbies.concat(newArr);
}
};
boy = new User();
girl = new User();
boy.addHobby("swimming");
girl.addHobby("running");
boy.hobbies
//["swimming"]
girl.hobbies
//["running"]
我们知道原因是因为作业。我们正在寻找完整解释为什么this.hobbies = this.hobbies.concat(newArr);
分配给实例级别而不是原型级别,尽管在这两种情况下都使用了术语this.hobbies
。
答案 0 :(得分:1)
这就是定义语言的方式。 From the spec:
生成MemberExpression:MemberExpression [Expression]的计算方法如下:
Ecma moon语言中没有提到在对象原型上寻找属性的任何提及。 l值成员表达式始终是指直接涉及的基础对象上的属性。
答案 1 :(得分:1)
使用“this”你不能为原型分配任何东西,但你可以从中读取。因此,当您执行this.hobbies = x;
时,您设置当前实例的属性“hobbies”而不是原型的属性,然后隐藏原型级别的同名属性(即,boy.hobbies
不再从原型返回数组,因为有一个带有此名称的直接属性。)
concat()
返回一个新数组,而不是对现有数组的引用,因此,您将隐藏原型级属性“hobbies”。
在下一次调用时,实例级数组“hobbies”将被包含先前值和新值的新数据覆盖。
答案 2 :(得分:1)
无论何时设置对象属性的值,都会在对象本身上定义属性,无论该属性是否存在于对象的原型链中。
规范section 8.7.2中描述了这一点:
4。如果
IsPropertyReference(V)
,则为 (a)If HasPrimitiveBase(V)
为false,然后将其作为 base 的[[Put]]
内部方法,否则请将其放在下面定义的特殊[[Put]]
内部方法中。登记/> (b)使用 base 作为此值调用put内部方法,并传递GetReferencedName(V)
作为属性名称,W
作为值,IsStrictReference(V)
表示投掷标志。
section 8.12.5中描述了[[Put]]
方法,其中重要的步骤是:
6。否则,在对象 O 上创建一个名为 P 的命名数据属性,如下所示
(a)让newDesc成为属性描述符{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}
。
(b)调用 O 的[[DefineOwnProperty]]
内部方法,将 P , newDesc 和投掷作为参数。
如果您仔细查看规范,您会看到,如果继承的属性不是访问者属性,则赋值只会在对象上创建属性。
即。以下实际上不会创建实例属性:
var name = 'foo';
function User() {}
Object.defineProperty(User.prototype, 'name', {
'get': function() { return name;},
'set': function(val) { name = val;}
});
var u1 = new Users();
var u2 = new Users();
u1.name = 'bar';
console.log(u2.name); // shows 'bar'
console.log(u1) // shows 'User {}' instead of 'User {name: 'bar'}'
答案 3 :(得分:0)
至于我,这不是Prototype enheritance的最好例子。
这是你的例子,由我修改:
function User() {
this.hobbies = [];
};
User.prototype = {
addHobby: function (x) {
this.hobbies.push(x);
}
};
boy = new User();
girl = new User();
boy.addHobby("swimming");
girl.addHobby("running");