2014-10-09 1 views
6

On the MDN strict mode reference page это говоритWeird поведение с «использовать строгий» и только для чтения свойства

Любое задание, которое молча терпит неудачу в обычном коде (присвоение незаписываемый собственности, присвоение геттерным только собственности, присвоение новое свойство на не расширяемый объект) выбросит в строгом режиме

Таким образом, используя свой пример, делать что-то вроде следующего кидает 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'); 

Runnin г 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, мы пытаемся установить свойство lolverbose. Чтобы доказать это, я изменил 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

ответ

3

См section 11.13.1 of the spec:

Когда присвоение происходит в строгом режиме кода, его LeftHandSide не должны оценивать с неразрешимой ссылки. Если при присваивании создается исключение ReferenceError. LeftHandSide также может не ссылаться на свойство данных со значением атрибута {[[Writable]]: false}, на свойство accessor со значением атрибута {[[Set]]: undefined}, а также на несуществующий свойство объекта, внутреннее свойство которого [[Extensible]] имеет значение false. В этих случаях генерируется исключение TypeError.

В примере кода, левая сторона на = выражения, на самом деле, ссылка на свойства данных с «записываемый» флаг установлен в false.

Теперь я несколько сочувствую тому, что он не должен применяться к унаследованным свойствам, но я вижу, что может быть сильный контраргумент. То, что объектный литерал позволяет создавать свойство как «собственное» свойство нового «сахара», конечно, кажется странным.

Редактировать — для ясности, проблема здесь в том, что присвоение объекту свойства всегда связано с присвоением «собственного» свойства объекта. Присвоения не влияют на свойства в цепочке наследования. Таким образом, поставленный вопрос включает в себя следующее очевидное противоречие: если свойство прототипа Object с флагом «writeable», установленным в false, предотвращает присвоение этому имени свойства на существующих объектах, почему это присвоение этому свойству успешно завершено в процессе оценки объектного литерала?

Для этого может быть хорошее обоснование, или это может быть ошибка. И V8, и то, что в настоящее время называют исполняемой средой Firefox (что-то вроде обезьяны), действуют одинаково.

+0

Есть ли у вас какие-либо мысли о том, почему 'sugar.lol' трактуется по-разному? – Tom

+0

@Tom Хорошо, вот что заставляет меня думать, что происходит что-то подозрительное. Тем не менее, я не знаю, какое поведение называть «неправильным» :) – Pointy

+1

@ Возможно, это связано с тем, что «сахаристая» вещь работает из-за того, как определяются выражения объектного литерала. Когда свойства определены таким образом, спецификация явно говорит о том, что вызывается '[[DefineOwnProperty]]'. – Pointy

0

Вы определили свойство на прототипах всех объектов, поэтому все они имеют свойство «lol» в своем прототипе.

Сахар определяется его собственным «лолом», поэтому он не имеет ничего общего с «лолом», который находится в его прототипе. Этот скрыт.

Verbose определяется как пустой объект, поэтому он будет иметь свойство «lol», доступное через его прототип. Следовательно, verbose.lol = ... не создает новое свойство, а изменяет его свойство прототипа, что вызывает ошибку, поскольку вы объявили ее не доступной для записи.

Я думаю, что все имеет смысл, если вы так думаете.

EDIT: это не правильный путь, чтобы увидеть его, прочитать комментарии

+0

На самом деле это не так, как все работает в JavaScript. Когда вы * присваиваете * свойству (забудьте о «записываемой» вещи на данный момент), вы ** никогда не изменяете никаких унаследованных свойств - вы всегда затрагиваете объект напрямую. То есть, когда вы назначаете свойство объекта, независимо от того, есть ли наследуемое свойство с указанным именем, вы всегда получаете «собственное» свойство целевого объекта. – Pointy

+1

Вы совершенно правы. Как указано в спецификации, строгий режим будет действовать так, как если бы вы пытались изменить свойство прототипа, но в конечном итоге оно изменит только свойство объекта, если все проверки в порядке. Это немного смешно. – Greg

Смежные вопросы