我正在阅读Google的JS optimization page,并进入了本节:
将实例变量声明/初始化放在原型上,用于具有值类型的实例变量...这样可以避免每次调用构造函数时不必要地运行初始化代码。
我想,"等等,然后你无法更改实例中的值"
但事实证明你可以,这让我感到惊讶。
为什么,当您更改原型提供给您的值时,是否会在对象本身上设置它?
x = { one: 1 }
y = Object.create(x)
y.one // 1, of course
y.one++;
y.one // 2
x.one // 1! why isn't this 2?
定义此行为的规则是什么?
是左手边查找总是在最近的对象上,但右手边查找是否会遍历原型链?
如果是这样,这将适用于引用和值类型,但是变异(y.arr.push(1)
)的诱惑可能太多了,因此Google建议仅针对值执行此操作?
答案 0 :(得分:3)
它有点复杂。
右侧:查找原型链,找到匹配的第一个(无论是getter还是数据属性),如果没有找到,则返回undefined
左手边:查找原型链,如果你发现了一个setter用途;否则,在对象本身上设置数据值。
如果简单的任务改变原型,那将是可怕的。 (想象一下,例如,International Prototype Kilogram自动改变重量,如果你削减一个你自己的公斤重量,以及随之而来的混乱。)
这是否只是一个明确的设定者,而不仅仅是任何一个可编辑的属性?我可以麻烦你问一个例子,它有一个更高的原型设定器和一个没有的例子吗?
var x = {
data: 1,
hidden: 1,
set accessor(val) {
this.hidden = val;
},
get accessor() {
return this.hidden;
}
};
var y = Object.create(x);
console.log("orig: x.data:", x.data, "; y.data:", y.data); // 1, 1
y.data = y.data + 1;
console.log("inc: x.data:", x.data, "; y.data:", y.data); // 1, 2
delete y.data; // removes y.data, so y.data is again looked up at x.data
console.log("del data: x.data:", x.data, "; y.data:", y.data); // 1, 1
console.log("----------");
console.log("orig: x.accessor:", x.accessor, "; y.accessor:", y.accessor); // 1, 1
y.accessor = y.accessor + 1;
console.log("inc: x.accessor:", x.accessor, "; y.accessor:", y.accessor); // 1, 2
console.log(" x.hidden:", x.accessor, "; y.hidden:", y.hidden); // 1, 2
delete y.accessor; // silent fail, y.accessor doesn't exist
console.log("del acc: x.accessor:", x.accessor, "; y.accessor:", y.accessor); // 1, 2
console.log(" x.hidden:", x.hidden, "; y.hidden:", y.hidden); // 1, 2
delete y.hidden;
console.log("del hidden: x.accessor:", x.accessor, "; y.accessor:", y.accessor); // 1, 1
console.log(" x.hidden:", x.hidden, "; y.hidden:", y.hidden); // 1, 1
您可以在此处看到的差异是,我们可以在y.data
之后删除y.data = ...
(数据属性),但我们无法在y.accessor
之后删除y.accessor = ...
,因为它不会不存在 - x.accessor
用于设置y.hidden
。
答案 1 :(得分:1)
那是因为属性赋值([[Set]]内部方法)到达原型对象x
上的属性,但接收者仍然是y
。然后,该属性在y
上定义。 x
上的媒体资源保持不变。
从技术上讲,作业y.one = 2
执行此操作:
y.[[Set]]("one", 2, y)
被召唤。y
没有"one"
个属性,因此调用会重定向到父y.[[GetPrototypeOf]]()
,x
。x.[[Set]]("one", 2, y)
被召唤。x
拥有"one"
自己的属性,这是一个数据属性(即不是getter / setter访问器)并且是可写的,因此在y
上定义了一个新属性y.[[DefineOwnProperty]]("one", PropertyDescriptor{[[Value]]: 2})
如果您不想这样,可以使用自定义访问者:
var one = 1;
var x = {
get one() {
return one;
},
set one(value) {
one = value;
}
};
var y = Object.create(x);
console.log(y.one); // 1
y.one++;
console.log(y.one); // 2
console.log(x.one); // 2