1) Почему console.trace возвращает только (анонимно)
Это поведение должно стать очевидным, как только вы поймете, что неправильно использовали console.trace (). Если вы перейдете к руководствам Mozilla и посмотрите на console.trace (), вы увидите, что он должен использоваться следующим образом:
function foo() {
function bar() {
console.trace();
}
bar();
}
foo();
Что дает следующий вывод:
bar
foo
<anonymous>
Другими словами, вы должны вызывать console.trace () из некоторой функции, а не заключать в нее вызов функции. Это потому, что console.trace () сначала проверяет имя функции, из которой она была вызвана ( bar ), затем имя вызывающей функции родителя ( foo ), затем имя его родителя и т. д., пока он не достигнет самого внешнего контекста выполнения, который является анонимной функцией - основным потоком выполнения, где начинается весь код.
Всякий раз, когда вы выполняете некоторый код внутри devtools (что, как я полагаю, вы используете), он всегда выполняется в этом глобальном контексте выполнения в форме оберточной анонимной функции. Так как вы вызываете console.trace () немедленно из этой анонимной функции, это то, что напечатано - anonymous .
Кроме того, console.trace () принимает параметры. Когда вы вызываете console.trace (), вы можете использовать любое количество аргументов, и эти аргументы просто выводятся на консоль непосредственно перед трассировкой стека. Поскольку Object.defineProperty () возвращает объект, для которого вы определяете новое свойство, это то, что вы передаете console.trace ().
TL; DR - console.trace () сначала печатает свои аргументы на консоли (т. Е. obj , то есть {a: 2, b: 4}); затем он продолжает печатать трассировку стека, которая состоит только из основного контекста выполнения, который является анонимной функцией при вызове кода из консоли.
2) this, defineProperty и все такое
Третий аргумент Object.defineProperty - это простой объект, , а не функция ! Его свойства разрешаются сразу во время инициализации, и только после этого они передаются в defineProperty. Это означает, что приведенное ниже функционально идентично вашему коду:
// First we initialize the third argument, separately from the call
// to .defineProperty()
var descriptor = {
value: this.a + 2,
enumerable: true
};
// at this point descriptor is already constructed. If you do
// console.log(descriptor.value) here, you will get NaN or maybe
// 4 or something else, depending on what you did before
Object.defineProperty(obj, 'b', descriptor);
// Here we simply invoke .defineProperty with descriptor as the third argument.
Вы также должны знать, что когда вы запускаете код в основном контексте выполнения (например, код вызывается из консоли devtools), this ссылается на объект Window . Окно не имеет свойства a, поэтому this.a не определено.
Что более интересно, this.a (или window.a) становится равным 2 после того, как вы выполните Object.prototype.a = 2. Это происходит потому, что Window, в конце концов, все еще является Object . Если вы назначите какое-либо свойство прототипу объекта объекта (!!!), это свойство будет доступно для всех объектов в вашем коде! Смешение? Да! На данный момент достаточно сказать, что то, что вы намеревались сделать (основываясь на вашем описании), было, вероятно, так:
obj.prototype = {a: 2};
или это:
var proto = {}; // First create a prototype
obj.prototype = proto; // Then assign it to the prototype property of your obj
obj.prototype.a = 2; // Now we can alter props on the prototype
Видишь разницу? Это имеет больше смысла? Конечно, после того, как вы это сделаете, ваш код больше не будет работать, потому что this.a снова станет неопределенным.
Результат таков: если вы будете идти так, вы не сможете добиться ожидаемого поведения. Дескрипторы свойств (аргумент № 3) на самом деле намного мощнее, чем просто комбинация значение и перечисляемый , и вы сможете получить желаемое поведение, если вы реализовали set () & методы get (). Кроме того, я считаю, что вы могли бы использовать прокси, чтобы получить аналогичное поведение. Не предоставляя больше подробностей, я вместо этого предлагаю сначала прочитать о контекстах выполнения функций, а затем немного больше о прототипах и, возможно, некоторые хорошие вопросы о переполнении стека о поведении this . После этого вы можете попробовать поискать предложенные решения.