为什么 this 绑定在其中一个箭头调用中,而不是另一个?

Why is this bound in one of these arrow invocations, but not the other?

我无法准确理解为什么 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调用arrayifythis(在arrayify内)有一个值。返回给 setProperty 的函数内的 this 也有一个值。

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

我无法理解第二种情况有何不同。特别令人困惑的是 thisnotsetPropertyFunc 的 init 中定义,但 在嵌入式函数中定义,而hoverPopupFunc

正好相反

我真的很想在这两种情况下都定义 this - 在第二种情况下如何实现? (在 hoverPopup 的情况下,必须进行一些初始化,所以它不像“对数组中的每个项目都这样做”那么简单)

查看调用返回函数的位置和方式以了解调用上下文 - 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) 部分 - 不是对象的一部分,而是一个独立的变量,因此没有调用上下文。

I'd really like this to be defined in both cases - how can I achieve that in the second case?

使用 .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); 将按照您的意愿传递它。

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

对象实例化后,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);
    },