cfh*_*cfh 25 javascript ecmascript-6 arrow-functions
我试图理解何时this在ES6箭头函数中进行词法绑定的规则.我们先来看看这个:
function Foo(other) {
other.callback = () => { this.bar(); };
this.bar = function() {
console.log('bar called');
};
}
Run Code Online (Sandbox Code Playgroud)
当我构造a时new Foo(other),在另一个对象上设置回调.回调是一个箭头函数,并且this箭头函数在词法上绑定到Foo实例,因此Foo即使我没有保留对Foo周围的任何其他引用,也不会进行垃圾回收.
如果我这样做会怎么样?
function Foo(other) {
other.callback = () => { };
}
Run Code Online (Sandbox Code Playgroud)
现在我将回调设置为nop,我从不提及this它.我的问题是:箭头功能是否仍然是词法绑定this,Foo只要活着就保持other活着,或者Foo在这种情况下可能是垃圾收集?
T.J*_*der 42
我的问题是:箭头函数是否仍然在词法上与此绑定,只要其他人活着就保持Foo活着,或者在这种情况下Foo可能被垃圾收集?
就规范而言,arrow函数具有对创建它的环境对象的引用,以及该环境对象具有的引用this,并且this引用Foo该调用创建的实例.因此,依赖于Foo未保存在内存中的任何代码都依赖于优化,而不是指定的行为.
重新优化,归结为您使用的JavaScript引擎是否优化了闭包,以及它是否可以在特定情况下优化闭包.(许多事情可以阻止它.)具有"正常"功能的情况就像这样:
function Foo(other) {
var t = this;
other.callback = function() { };
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,函数会关闭包含的上下文t,因此理论上它会引用一个参数t,然后将Foo实例保存在内存中.
这就是理论,但在实践中,现代JavaScript引擎可以看到t闭包不使用它并且可以优化它,前提是这样做不会引入可观察到的副作用.它是否确实如此,何时,何时完全取决于发动机.
由于箭头函数确实是词法闭包,因此情况完全类似,因此您希望JavaScript引擎执行相同的操作:除非它导致可以观察到的副作用,否则将其优化.也就是说,请记住,箭头函数是非常新的,所以很可能引擎在这方面没有太多优化(没有双关语).
V8的Chrome浏览器版本(我使用Chrome 48.0.2564.116 64位)确实目前看来做到这一点:我跑的Chrome,可让您强制垃圾收集(模式google-chrome --js-flags="--expose-gc"),并运行此:
"use strict";
function Foo(other) {
other.callback = () => this; // <== Note the use of `this` as the return value
}
let a = [];
for (let n = 0; n < 10000; ++n) {
a[n] = {};
new Foo(a[n]);
}
// Let's keep a Foo just to make it easy to find in the heap snapshot
let f = new Foo({});
log("Done, check the heap");
function log(msg) {
let p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}Run Code Online (Sandbox Code Playgroud)
在Dev Tools中,获取堆快照显示Foo内存中预期的10,001个实例.然后我gc()在控制台中执行(这是强制垃圾收集的方式)并拍摄另一个堆快照.10,001个Foo实例仍在那里:
然后我更改了回调,因此它没有引用this:
other.callback = () => { }; // <== No more `this`
Run Code Online (Sandbox Code Playgroud)
"use strict";
function Foo(other) {
other.callback = () => {}; // <== No more `this`
}
let a = [];
for (let n = 0; n < 10000; ++n) {
a[n] = {};
new Foo(a[n]);
}
// Let's keep a Foo just to make it easy to find in the heap snapshot
let f = new Foo({});
log("Done, check the heap");
function log(msg) {
let p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}Run Code Online (Sandbox Code Playgroud)
并再次运行页面.我甚至没有gc(),Foo当代码完成运行时,内存中只有一个实例(我放在那里以便在快照中找到它):
我想知道回调是否完全为空以便进行优化这一事实,并且惊喜地发现它并非如此:Chrome很高兴保留部分关闭而放弃this,如下所示:
"use strict";
function Foo(other, x) {
other.callback = () => x * 2;
}
let a = [];
for (let n = 0; n < 10000; ++n) {
a[n] = {};
new Foo(a[n], n);
}
// Let's keep a Foo just to make it easy to find in the heap snapshot
let f = new Foo({}, 0);
document.getElementById("btn-call").onclick = function() {
let r = Math.floor(Math.random() * a.length);
log(`a[${r}].callback(): ${a[r].callback()}`);
};
log("Done, click the button to use the callbacks");
function log(msg) {
let p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}Run Code Online (Sandbox Code Playgroud)
<input type="button" id="btn-call" value="Call random callback">Run Code Online (Sandbox Code Playgroud)
尽管回调存在且有参考x,但Chrome会优化Foo实例.
您询问了有关如何this在箭头函数中解析的规范参考:该机制遍布整个规范.每个环境(例如通过调用函数创建的环境)都有一个[[thisBindingStatus]]内部插槽,"lexical"用于箭头功能.在确定值时this,使用内部操作,该操作ResolveThisBinding使用内部GetThisEnviroment操作来查找已this定义的环境.当进行"正常"函数调用时,如果环境不是环境,BindThisValue则用于绑定this函数调用"lexical".因此我们可以看到,this从箭头函数中解析就像解析变量一样:检查当前环境是否有this绑定,而不是找到一个(因为this在调用箭头函数时没有绑定),它会进入包含环境.
| 归档时间: |
|
| 查看次数: |
1242 次 |
| 最近记录: |