为啥这被绑定在这些箭头调用之一中,而不是另一个?

Posted

技术标签:

【中文标题】为啥这被绑定在这些箭头调用之一中,而不是另一个?【英文标题】:Why is this bound in one of these arrow invocations, but not the other?为什么这被绑定在这些箭头调用之一中,而不是另一个? 【发布时间】:2021-07-13 05:38:24 【问题描述】:

我无法准确理解为什么 this 在某些情况下具有价值,而在其他情况下则没有。我维护了一个库,为了解释我的困惑,它被精简到最低限度,如下所示:

const arrayify = f => 
    console.log('init', f.name, !!this);
    return function (thingOrThings, ...args) 
        console.log('arrayify for ', f.name, !!this);
        return [thingOrThings].map(t => f.call(this, t, ...args));
    ;
;
class Utils 
Object.assign(Utils.prototype, 
    setProperty: arrayify(function setPropertyFunc(layer, prop, value) 
        console.log('setPropertyFunc called', !!this); // outputs true
    ),
    hoverPopup(layers, cb, popupOptions = ) 
        console.log('hoverPopup init', !!this);
        arrayify(function hoverPopupFunc(layer, cb) 
            console.log('hoverPopupFunc called', !!this); // outputs false
        )(layers, cb);
    ,
);

(它看起来过于复杂,因为我删除了所有实际有用的东西。但基本上arrayify 允许函数采用单个事物或事物数组,并隐式运行在数组中的每个项目上后一种情况。)

我这样称呼它:

const U = new Utils();

U.setProperty('mylayer', 'lineColor', 'red');
U.hoverPopup('mylayer', () => 1);

输出:

init setPropertyFunc false
arrayify for  setPropertyFunc true
setPropertyFunc called true
hoverPopup init true
init hoverPopupFunc false
arrayify for  hoverPopupFunc false
hoverPopupFunc called false

所以在第一种情况下,调用U.setProperty 调用arrayify,并且this(在arrayify 内部)有一个值。同样this在返回给setProperty的函数内部也有一个值。

第二个,U.hoverPopup 调用arrayify 没有this 值,返回的函数中也没有this 值。

我无法理解第二种情况的不同之处。尤其令人困惑的是,this 没有setPropertyFunc 的 init 中定义,但 在嵌入函数中定义,而 hoverPopupFunc 的情况正好相反

我真的很希望在这两种情况下都定义this - 在第二种情况下我怎样才能实现呢? (在hoverPopup 的情况下,必须进行一些初始化,所以它并不像“对数组中的每个项目都执行此操作”那么简单)

【问题讨论】:

【参考方案1】:

添加我自己的解释以帮助更好地理解这一点。

对象实例化后,U基本上是这样的:


    setProperty: function (thingOrThings, ...args) 
        console.log('arrayify for ', f.name, !!this);
        return [thingOrThings].map(t => /* ... */);
    ,
    hoverPopup: function(layers, cb, popupOptions = ) 
        console.log('hoverPopup init', !!this);
        arrayify(function hoverPopupFunc(layer, cb) 
            console.log('hoverPopupFunc called', !!this);
        )(layers, cb);
    

所以,此时,setProperty 只是对象上的一个函数,所以当它被调用时,它当然具有this 的值。这个函数是由另一个函数(无论是否箭头)创建的这一事实是无关紧要的。

hoverPopup也是对象上的一个函数,也有一个this的值。但是当它调用arrayify 时,该调用具有this 值,因为它不满足任何the four rules

试图将this 的值绑定到arrayify 是行不通的,因为它是一个箭头函数。但是你可以给arrayify返回的函数绑定一个值:

    hoverPopup(layers, cb, popupOptions = ) 
        console.log(`hoverPopup init $this === U`);
        arrayify(function hoverPopupFunc(layer, cb) 
            console.log(`hoverPopupFunc called $this === U`); // outputs true
        ).call(this, layers, cb);
    ,

【讨论】:

【参考方案2】:

查看调用返回函数的位置和方式以了解调用上下文 - this

在第一种情况下,原型上的setProperty 方法是返回的函数,它被作为实例的属性调用

    U.setProperty('mylayer', 'lineColor', 'red'); // outputs true
//  ^  calling context: this

但在第二种情况下,没有调用上下文:

arrayify(function (layer, cb) 
    console.log('hoverpopup', !!this); // outputs false
)(layers, cb);

这只是一个可以立即调用的普通函数。简单一点:

arrayify(someFn)(layers, cb)

被调用的函数 - 整个 arrayify(someFn) 部分 - 不是对象的一部分,而是一个独立的变量,因此没有调用上下文。

我真的很想在这两种情况下都定义这个 - 在第二种情况下我怎样才能做到这一点?

使用.call 调用具有特定this 的函数。

hoverPopup(layers, cb, popupOptions = ) 
    // do some other initialisation here
    const fn = function (layer, cb) 
        console.log('hoverpopup', !!this); // outputs false
    ;
    arrayify(fn).call(this, layers, cb);
,

这会起作用,因为当在 hoverPopup 的主体内时,this 是实例:

    U.hoverPopup('mylayer', () => 1);
//  ^ calling context: this

这样做arrayify(fn).call(this, layers, cb); 会随心所欲地传递它。

【讨论】:

啊,我明白了。这很有意义。我曾尝试使用.call(),但放错了位置。

以上是关于为啥这被绑定在这些箭头调用之一中,而不是另一个?的主要内容,如果未能解决你的问题,请参考以下文章

为啥方法局部静态变量绑定到类而不是实例?

WPF)为啥完全相同的绑定在一个地方工作,而在另一个地方却不行?

箭头函数总结

为啥 setattr 在绑定方法上失败

C#winform中为啥一个窗体的对象可以调用在另一个窗体中创建的一个类未实例化下

react表单和绑定事件及state和props-04