“使用严格”和只读属性的奇怪行为
Posted
技术标签:
【中文标题】“使用严格”和只读属性的奇怪行为【英文标题】:Weird behaviour with 'use strict' and read only properties 【发布时间】:2014-12-05 00:08:39 【问题描述】:On the MDN strict mode reference page 它说
任何在正常代码中静默失败的赋值(赋值给不可写属性、赋值给 getter-only 属性、赋值给不可扩展对象的新属性)都将进入严格模式
因此,使用他们的示例,执行以下操作会引发 TypeError
"use strict";
var obj1 = ;
Object.defineProperty(obj1, "x", value: 42, writable: false );
obj1.x = 9; // throws a TypeError
但是,我遇到了一个示例,其中“使用严格”似乎对这条规则有点过分热心。这是我的设置
definelol.js
Object.defineProperty(Object.prototype, 'lol',
value: 'wat'
)
setlol.js
'use strict';
console.log('here 0');
var sugar = lol: '123'
console.log('here 1');
var verbose = ;
verbose.lol = '123';
console.log('here 2');
console.log('sugar.lol:', sugar.lol);
console.log('verbose.lol:', verbose.lol);
console.log('Object.prototype.lol:', Object.prototype.lol);
app.js
require('./definelol.js');
require('./setlol.js');
运行node app.js
给出
here 0
here 1
/pathto/setlol.js:10
verbose.lol = '123';
^
TypeError: Cannot assign to read only property 'lol' of #<Object>
at Object.<anonymous> (/pathto/setlol.js:10:13)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (/pathto/app.js:2:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
这个输出有几个有趣的地方。首先是我们没有尝试在Object.prototype
上设置lol
属性,而是在尝试设置verbose
的lol
属性。为了证明这一点,我将definelol.js
更改为
Object.defineProperty(Object.prototype, 'lol',
writable: true,
value: 'wat'
)
现在,运行 node app.js
给出
here 0
here 1
here 2
sugar.lol: 123
verbose.lol: 123
Object.prototype.lol: wat
有趣的第二件事是原始程序在 verbose.lol = '123'
上失败了,但非常高兴创建 sugar
并将其 lol
设置为 123。我不明白这一点,因为看起来我们创建的方式sugar
应该只是我们创建 verbose
方式的语法糖
【问题讨论】:
【参考方案1】:见section 11.13.1 of the spec:
当在严格模式代码中发生赋值时,其 LeftHandSide 不得评估为无法解析的引用。如果确实如此,则在分配时会引发 ReferenceError 异常。 LeftHandSide 也可能不是对具有属性值 [[Writable]]:false 的数据属性的引用,对具有属性值 [[Set]]:undefined 的访问器属性的引用,也可能不是对不存在的引用[[Extensible]] 内部属性值为 false 的对象的属性。在这些情况下,会引发 TypeError 异常。
在您的示例代码中,=
表达式的左侧实际上是对“可写”标志设置为 false
的数据属性的引用。
现在我有点同情它不应该应用于继承属性的概念,但我可以看到可能存在强烈的反驳。对象字面量允许将属性创建为新“糖”对象的“自己”属性,这当然看起来很奇怪。
edit — 为清楚起见,这里的问题是对对象属性的分配始终是关于分配给对象的“自己的”属性。赋值不会影响继承链上的属性。因此,提出的问题涉及以下明显的矛盾:如果 Object 原型中的“可写”标志设置为 false
的属性阻止分配给 现有 对象上的该属性名称,为什么会这样?在评估对象字面量的过程中分配给该属性成功?
这可能有一个很好的理由,或者它可能是一个错误。 V8 和任何当前调用的 Firefox 运行时(我猜是猴子)都以相同的方式运行。
【讨论】:
你有什么想法为什么sugar.lol
会被区别对待吗?
@Tom 嗯,这就是让我觉得有些可疑的原因。但是,我不知道将哪种行为称为“错误”:)
@Tom 可能是因为对象文字表达式的定义方式,“糖”的作用。当以这种方式定义属性时,规范明确指出调用了[[DefineOwnProperty]]
。【参考方案2】:
您在每个对象的原型上都定义了一个属性,因此所有对象在其原型中都有一个“lol”属性。
Sugar 是用他自己的“lol”定义的,所以这与它原型中的“lol”无关。那个是隐藏的。
Verbose 被定义为一个空对象,因此,它将具有可通过其原型访问的“lol”属性。因此verbose.lol = ...
不是创建新属性,而是修改其原型的属性,当您声明它不可写时会引发错误。
如果你这样想,我认为这一切都是有道理的。
编辑:这不是正确的查看方式,请阅读 cmets
【讨论】:
这实际上并不是 javascript 中的工作方式。当您分配 一个属性时(暂时忘记“可写”的事情),您从不 修改任何继承的属性——您总是直接影响对象。也就是说,当您分配给对象的属性时,无论是否存在具有引用名称的可继承属性,您最终都会得到目标对象的“自己的”属性。 你说的很对。正如您引用的规范所说,严格模式将就像您试图修改原型的属性一样,但如果所有检查都正常,它最终只会修改对象的属性。这有点好笑。以上是关于“使用严格”和只读属性的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章