使用'use strict'和只读属性的奇怪行为

时间:2014-10-09 22:27:20

标签: javascript node.js use-strict defineproperty

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)

这个输出有一些有趣的事情很有意思。首先,我们不会尝试在lol上设置Object.prototype属性,我们正在尝试设置lol verbose属性。为了证明这一点,我将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的方式的语法糖

2 个答案:

答案 0 :(得分:3)

请参阅section 11.13.1 of the spec:

  

当在严格模式代码中发生赋值时,其LeftHandSide不能求值为不可解析的引用。如果确实如此,则在赋值时抛出ReferenceError异常。 LeftHandSide也可能不是对具有属性值{[[Writable]]:false}的数据属性的引用,而是对具有属性值{[[Set]]:undefined}的访问者属性的引用,也不是对不存在的引用属性的引用[[Extensible]]内部属性值为false的对象的属性。在这些情况下,会抛出TypeError异常。

在您的示例代码中,=表达式的左侧实际上是对带有&#34;可写&#34;的数据属性的引用。标志设置为false

现在我有点同情它不应该适用于继承的属性,但我可以看到可能存在强烈的反驳。对象文字允许将属性创建为&#34; own&#34;新糖的特性&#34;糖对象当然看起来很奇怪。

编辑 - 为清楚起见,这里的问题是对对象属性的赋值总是关于分配给自己的&#34;对象的属性。作业不会影响继承链的属性。因此,提出的问题涉及以下明显的矛盾:如果来自Object原型的属性与&#34;可写&#34;设置为false的标志会阻止在现有对象上分配该属性名称,为什么在评估对象文字的过程中对该属性的分配成功 ?

这可能有一个很好的理由,或者它可能是一个错误。 V8和当前调用的任何Firefox运行时(我猜的是某些东西)都采用相同的方式。

答案 1 :(得分:0)

您在每个对象的原型上定义了一个属性,因此它们的原型中都有一个“lol”属性

Sugar是用他自己的'lol'定义的,所以这与其原型中的'lol'无关。那个是隐藏的。

详细定义为空对象,因此,它将通过其原型访问“lol”属性。因此verbose.lol = ...不会创建新属性,而是修改其原型的属性,当您声明它不可写时会引发错误。

我认为如果你这么想就行了。

编辑:这不是查看它的正确方法,请阅读评论