callstack 和 this 在 .defineProperty 方法期间的行为
Behaviour of callstack and this during .defineProperty method
前言:
Feel free to skip to the actual question below, if you find the
'backstory' here unnecessary. But I do believe it adds good amount of
detail and further context for the question
最近我一直在尝试对象和 [[ Prototype ]]
链,
首先,我很困惑为什么我的 this
引用 return 在 NaN
中使用以下代码编辑:
var obj = {
a: 2
}
Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
});
console.log(obj.b);
首先,我对为什么感到困惑,但后来 NaN
行为让我震惊。
我的 obj.b
计算为 undefined + 2
,因此 return 在 NaN
中
现在,显然更改代码以显式引用 value: obj.a + 2
就可以解决问题,但我想进一步调查为什么 this.a
returns 为 undefined
.
基本上,我的问题是我的调用堆栈引用了 Object.prototype.a
,因为 Object.defineProperty
是隐式委托的 Object.prototype.defineProperty
。
因此,将 a
明确设置为 Object.prototype
的 属性 将解决此问题:
var obj = {
a: 2
}
Object.prototype.a = obj.a;
Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
});
console.log(obj.b);
这 return 是 4
的预期结果(虽然是的,创建一个 属性 a
到整个 Object.prototype
是绝对不是最好的编程实践,但出于说明目的,它会很好地服务)
问题:
然而,让我感到困惑的是,如果我 运行 在表达式上使用 console.trace
,我的调用堆栈完全由 (anonymous)
组成 - 所以基本上 global
(默认)this
关键字的隐式绑定。
You will have to run the code in your own console, as sadly console.trace()
is unfortunately not supported by jsfiddle (least to my knowledge)
var obj = {
a: 2
}
Object.prototype.a = obj.a;
console.trace(Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
}));
TL/DR:
因此,将这个问题总结为两个快速要点:
- 为什么调用堆栈 return 仅
(anonymous)
而不是 Object.prototype
- 在我们的
.defineProperty()
方法中将 this
显式绑定到引用对象 obj
的正确方法是什么?
提前致谢。
1) 为什么 console.trace returns 仅(匿名)
一旦您意识到自己错误地使用了 console.trace(),这种行为就会变得很明显。如果你去 Mozilla 的手册并查找 console.trace() 你会看到它应该像这样使用:
function foo() {
function bar() {
console.trace();
}
bar();
}
foo();
给出以下输出:
bar
foo
<anonymous>
换句话说,您必须从 内部调用 console.trace() 某个函数,而不是用它包装函数调用。这是因为 console.trace() 首先检查调用它的函数的名称 (bar),然后是父调用函数的名称 (foo ),然后是其父级的名称,依此类推,直到它到达最外层的执行上下文,这是一个 anonymous 函数 - 主执行线程,所有代码都从这里开始.
每当你在 devtools 中执行一些代码(我假设你正在使用它)时,它总是在这个全局执行上下文中以包装匿名函数的形式执行。由于您立即从此匿名函数调用 console.trace(),因此打印的内容是 - anonymous.
另外,console.trace() 接受参数。当您调用 console.trace() 时,您可以使用任意数量的参数,这些参数会在堆栈跟踪之前简单地打印到控制台。由于 Object.defineProperty() returns 您正在为其定义新 属性 的对象,这就是您传递给 console.trace() 的对象。
TL;DR - console.trace() 首先将其参数打印到控制台(即 obj,即 {一个:2,乙:4});然后它继续打印堆栈跟踪,它仅包含主要执行上下文,当您从控制台调用代码时,它是一个匿名函数。
2) 这个,定义属性 等等
Object.defineProperty的第三个参数是一个简单的对象,不是函数!它的属性在初始化期间立即解析,然后才传递到 define属性。这意味着以下代码在功能上与您的代码相同:
// 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对象。 Window 没有 属性 a,这就是 this.a 未定义的原因。
更有趣的是this.a(或window.a)在你Object.prototype.a = 2之后变成了2。发生这种情况是因为Window仍然是一个[=毕竟48=]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又会变成undefined。
结果如下:按照这种方式您无法实现您期望的行为。 属性 描述符(参数 #3)实际上比 value 和 enumerable 的组合更强大,你应该能够如果您在其上实现了 set() 和 get() 方法,则可以获得您想要的行为。另外,我相信您可以使用代理来获得类似的行为。没有提供更多细节,因为我建议您先阅读函数执行上下文,然后再阅读更多关于原型的内容,也许还有一些关于堆栈溢出的关于 this 行为的好问题。之后您可以尝试查看建议的解决方案。
前言:
Feel free to skip to the actual question below, if you find the 'backstory' here unnecessary. But I do believe it adds good amount of detail and further context for the question
最近我一直在尝试对象和 [[ Prototype ]]
链,
首先,我很困惑为什么我的 this
引用 return 在 NaN
中使用以下代码编辑:
var obj = {
a: 2
}
Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
});
console.log(obj.b);
首先,我对为什么感到困惑,但后来 NaN
行为让我震惊。
我的 obj.b
计算为 undefined + 2
,因此 return 在 NaN
现在,显然更改代码以显式引用 value: obj.a + 2
就可以解决问题,但我想进一步调查为什么 this.a
returns 为 undefined
.
基本上,我的问题是我的调用堆栈引用了 Object.prototype.a
,因为 Object.defineProperty
是隐式委托的 Object.prototype.defineProperty
。
因此,将 a
明确设置为 Object.prototype
的 属性 将解决此问题:
var obj = {
a: 2
}
Object.prototype.a = obj.a;
Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
});
console.log(obj.b);
这 return 是 4
的预期结果(虽然是的,创建一个 属性 a
到整个 Object.prototype
是绝对不是最好的编程实践,但出于说明目的,它会很好地服务)
问题:
然而,让我感到困惑的是,如果我 运行 在表达式上使用 console.trace
,我的调用堆栈完全由 (anonymous)
组成 - 所以基本上 global
(默认)this
关键字的隐式绑定。
You will have to run the code in your own console, as sadly
console.trace()
is unfortunately not supported by jsfiddle (least to my knowledge)
var obj = {
a: 2
}
Object.prototype.a = obj.a;
console.trace(Object.defineProperty(obj, 'b', {
value: this.a + 2,
enumerable: true
}));
TL/DR:
因此,将这个问题总结为两个快速要点:
- 为什么调用堆栈 return 仅
(anonymous)
而不是Object.prototype
- 在我们的
.defineProperty()
方法中将this
显式绑定到引用对象obj
的正确方法是什么?
提前致谢。
1) 为什么 console.trace returns 仅(匿名)
一旦您意识到自己错误地使用了 console.trace(),这种行为就会变得很明显。如果你去 Mozilla 的手册并查找 console.trace() 你会看到它应该像这样使用:
function foo() {
function bar() {
console.trace();
}
bar();
}
foo();
给出以下输出:
bar
foo
<anonymous>
换句话说,您必须从 内部调用 console.trace() 某个函数,而不是用它包装函数调用。这是因为 console.trace() 首先检查调用它的函数的名称 (bar),然后是父调用函数的名称 (foo ),然后是其父级的名称,依此类推,直到它到达最外层的执行上下文,这是一个 anonymous 函数 - 主执行线程,所有代码都从这里开始.
每当你在 devtools 中执行一些代码(我假设你正在使用它)时,它总是在这个全局执行上下文中以包装匿名函数的形式执行。由于您立即从此匿名函数调用 console.trace(),因此打印的内容是 - anonymous.
另外,console.trace() 接受参数。当您调用 console.trace() 时,您可以使用任意数量的参数,这些参数会在堆栈跟踪之前简单地打印到控制台。由于 Object.defineProperty() returns 您正在为其定义新 属性 的对象,这就是您传递给 console.trace() 的对象。
TL;DR - console.trace() 首先将其参数打印到控制台(即 obj,即 {一个:2,乙:4});然后它继续打印堆栈跟踪,它仅包含主要执行上下文,当您从控制台调用代码时,它是一个匿名函数。
2) 这个,定义属性 等等
Object.defineProperty的第三个参数是一个简单的对象,不是函数!它的属性在初始化期间立即解析,然后才传递到 define属性。这意味着以下代码在功能上与您的代码相同:
// 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对象。 Window 没有 属性 a,这就是 this.a 未定义的原因。
更有趣的是this.a(或window.a)在你Object.prototype.a = 2之后变成了2。发生这种情况是因为Window仍然是一个[=毕竟48=]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又会变成undefined。
结果如下:按照这种方式您无法实现您期望的行为。 属性 描述符(参数 #3)实际上比 value 和 enumerable 的组合更强大,你应该能够如果您在其上实现了 set() 和 get() 方法,则可以获得您想要的行为。另外,我相信您可以使用代理来获得类似的行为。没有提供更多细节,因为我建议您先阅读函数执行上下文,然后再阅读更多关于原型的内容,也许还有一些关于堆栈溢出的关于 this 行为的好问题。之后您可以尝试查看建议的解决方案。