Javascript:重用有界函数

Javascript: reuse bounded functions

本文关键字:函数 Javascript      更新时间:2023-09-26

标准Function.prototype.bind方法在每次调用时创建一个新函数。我需要存储在某个地方并重用该函数的有界变体。

实现适用于对象参数的函数bindArg不是火箭科学:

const fnStorage = new WeakMap();
function bindArg(fn, arg) {
  if (!fnStorage.has(fn)) {
    fnStorage.set(fn, new WeakMap());
  }
  if (!fnStorage.get(fn).has(arg)) {
    fnStorage.get(fn).set(arg, fn.bind(null, arg));
  }
  return fnStorage.get(fn).get(arg);
}

此解决方案工作正常,但仅适用于对象参数。可以通过将WeakMap标量参数更改为 Map 来完成标量参数。但这根本不好,因为Map会保留对函数的有界变体的引用并防止垃圾回收它。

有没有办法实现bindArg函数,它是完全不可变的,不会为任何类型的参数泄漏内存?

您可以将Map用于基元数据类型,WeakMap用于对象:

function isPrimitive(value) {
    return Object(value) !== value;
}
var primStore = new Map;
var objStore = new WeakMap;
function bindArg(fn, arg) {
    var store = isPrimitive(arg) ? primStore : objStore;
    if (!store.has(arg)) store.set(arg, new WeakMap);
    if (!store.get(arg).has(fn)) store.get(arg).set(fn, fn.bind(null, arg));
    return store.get(arg).get(fn);
}

请注意,我们返回 store.get(arg).get(fn) 而不是 store.get(fn).get(arg) 。这种翻转对bindArg函数的实际语义没有任何影响,但当我们想要区分原始数据类型和对象时,这是必要的。


编辑:或者,您可以创建一个新的WeakMap2数据结构,该结构将基元值存储在Map中,将对象存储在WeakMap中,如下所示:

var WeakMap2 = defclass({
    constructor: function () {
        this.map1 = new Map;
        this.map2 = new WeakMap;
        if (arguments.length > 0) {
            for (var object in argument[0]) {
                if (isPrimitive(object))
                    throw new TypeError("Iterator value " +
                        object + " is not an entry object");
                else this.set(object[0], object[1]);
            }
        }
    },
    get: function (key) {
        return (isPrimitive(key) ? this.map1 : this.map2).get(key);
    },
    set: function (key, value) {
        return (isPrimitive(key) ? this.map1 : this.map2).set(key, value);
    },
    has: function (key) {
        return (isPrimitive(key) ? this.map1 : this.map2).has(key);
    },
    delete: function (key) {
        return (isPrimitive(key) ? this.map1 : this.map2).delete(key);
    }
});
function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}
function isPrimitive(value) {
    return Object(value) !== value;
}

但是,我不推荐它,因为它增加了一个抽象层,使你的代码变慢。