with(new Proxy({}, {
has() { return true },
get(obj, key, proxy) { return console.log(String(key)) } })
) {
a--
}
Вывод в Chrome:
Symbol(Symbol.unscopables)
a
Symbol(Symbol.unscopables)
Вывод в Firefox:
Symbol(Symbol.unscopables)
Symbol(Symbol.unscopables)
a
Насколько я понимаю, одно обращение связано с чтением, а другое с записью.
Логично, что конструкция a--
должна записать значение в то же место, откуда прочитала. А двойное чтение из Symbol.unscopables
как бы намекает, что это не так и у меня есть возможность отдать нечто для чтения, но сказать, что в мой объект этого записывать не надо?
Неужели это так и задумано? Что на эту тему говорит стандарт?
Собственно, в Хроме и FF такое почти прокатывает - чтение и запись связаны с разными объектами, однако работает по-разному:
var a, b, flag = true
with (a = { x: 7 })
with (b = { x: 4, get [Symbol.unscopables]() { return { x: flag=!flag } } })
x++
// Chrome FF
console.log(a) // {x:5} {x:7}
console.log(b) // {x:4} {x:8}
PS: Этот вопрос по-английски.
Обратимся к спецификации, в таблице 1 находится описание символа @@unscopables
An object valued property whose own property names are property names that are excluded from the with environment bindings of the associated object.
Объект, в котором названия свойств совпадают со свойствами исключенными из with связывания.
Значение этого свойства проверяется в функции HasBindings(N)
Здесь нас интересуют пункты начиная с 6. Если проверяемое свойство N
находится в блоке with
, оно всегда проверяется в объекте unscopables.
Теперь перейдем к постфиксному декременту. В алгоритме есть два ключевых момента:
Каждая из этих функций в итоге вызывает описанную выше HasBinding.
В обновленном пример добавлена функция set
и в логах видно, что второй раз вызывается как раз перед ней.
var value = 10;
with(new Proxy({}, {
has(o, k) {
console.log('has', String(k));
return true; // k !== 'console';
},
get(obj, key, proxy) {
console.log('get', String(key));
if (key === Symbol.unscopables) {
return {
a: false,
console: true
}
}
return value;
},
set(o, k, v, proxy) {
console.log('set', k, v);
if (k == 'a') value = v;
}
})) {
a--;
}
Что характерно firefox
сначала получает все связанные поля, и лишь затем получает значения свойств.
Далее можно разобрать второй пример:
var a, b, flag = true
var valueA = 7,
valueB = 4;
with(a = {
get x() {
console.log('get A', flag);
return valueA;
}, set x(v) {
console.log('set A', flag);
valueA = v;
}
})
with(b = {
get x() {
console.log('get B', flag);
return valueB;
}, set x(v) {
console.log('set B', flag);
valueB = v;
},
get [Symbol.unscopables]() {
var t = {
x: flag = !flag
};
console.log('Symbol.unscopables', t);
return t;
}
})
x++
// Chrome FF
console.log(valueA) // {x:5} {x:7}
console.log(valueB) // {x:4} {x:8}
Что тут происходит?
x
в текущем with
или нет. Значение флага меняется на противоположное.Как можно заметить по логам, сначала значения флага возвращается false
, что в соответствии со спецификацией указывает на то, что данное имя находится в текущем скопе. Поэтому значение берется из объекта b
.
Далее флаг выставляется в true
и при вычслении в каком скопе находится x
в котороый надо положить, получаем, что он находится не текущем скопе, поэтому значение сохраняется в объекте a
.
Как видно - Chrome работает в соответствии со спецификацией.
Что касается firefox. Как можно заметить, firefox не инвертирует значение возвращенное в объекте unscopables, как это указано в спецификации, поэтому значение сначала берется из внешнего with
и присваивается значению из внутреннего.
По результатам проверок, можно сделать вывод, что firefox сначала получает свойство куда записывать, и лишь затем свойство откуда брать значение. Из-за этого в примере, когда значение flag меняется на каждый вызов, происходит расхождение с работой Chrome и EDGE.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Метод push - ничего не делает для объекта, потому по умолчанию у объектов нет такого метода
Нужно найти все неповторяющиеся символы или по-другому - отсеять повторяющиеся символы