JavaScript - 代理对象的行为类似于原始对象

Tib*_*agu 5 javascript metaprogramming object javascript-objects ecmascript-6

我正在尝试构建一个系统,在加载库之前“缓存”对该库的调用。

这类似于 Google Analytics“设置”代码对_gaq变量所做的操作 - 它被初始化为一个数组,“缓存”对真实分析端点的调用,直到ga.js库加载为止。当它发生时,它会读取_gaq并重播呼叫。

我们决定这样做是因为我们的遗留代码包含对特定库的大量调用,该库在<head>. 这大大增加了首次内容绘制的时间,因为需要评估和执行大量 JS。

但是,代码中有太多地方需要更改(包装在'DOMContentLoaded'侦听器中),因此我们决定尝试使用解决方法。

我们决定尝试使用Proxy捕获对我们库方法的调用,并在准备好后重播它们:

// Original code:
var ourLib = new OurLib({ ... });

// Throughout the site, calls such as:
var res1 = ourLib.doThis();
var res2 = ourLib.getThat(3);
Run Code Online (Sandbox Code Playgroud)

这是我们的新代码正在执行的操作的“简化”版本:

// New code:
var ourLib = new Proxy({
    calls: [],
}, {
    get(target, prop) {
        if (prop in target) {
            return Reflect.get(...arguments);
        }

        const callref = { prop, args: [], placeholder };
        target.calls.push(callref);

        return function(...args) {
            const placeholder = MakeResultPlaceholder(...);

            callref.args = args;
            callref.placeholder = placeholder;

            return placeholder;
        };
    },
});

// Throughout the site, calls continue as before, except now they're 'stored' in `calls`
var res1 = ourLib.doThis();
var res2 = ourLib.getThat(3);

// Much later, the original lib is loaded, and 
var ourRealLib = new OurLib({ ... });
__playbackCalls(ourLib.calls, ourRealLib);

// Replace the proxy with the real thing
ourLib = ourRealLib;
Run Code Online (Sandbox Code Playgroud)

上面运行后,calls属性将是这样的:

[
    {
        prop: 'doThis',
        args: [],
        reference: ResultPlaceholder
    },
    {
        prop: 'getThat',
        args: [3],
        reference: ResultPlaceholder
    }
]
Run Code Online (Sandbox Code Playgroud)

__playbackCalls函数迭代数组calls和每个对象中存储的apply每个方法。ourRealLibargs

calls.forEach(({ prop, args, reference }) => {
    reference._value = ourRealLib[prop].apply(ourRealLib, args);
});
Run Code Online (Sandbox Code Playgroud)

当需要使用代理调用的结果时,问题就出现了。现在,正如您所看到的,调用返回一个placeholder对象(它本身就是另一个代理)。这些占位符拥有_value在“播放”期间填充的属性。

所以这是问题:

  • 假设ourLib.getThat()要返回一个number.
  • 在第一次“运行”期间,由于整个代理的原因,res1将指向一个placeholder对象:Proxy { _value: undefined }
  • 真正的库已加载,“播放”完成,ourRealLib.getThat(3)返回23,所以res1将是Proxy { _value: 23 }
  • 我可以做些什么以便我们可以用作res1号码吗?就像是:
console.log(res1 * 2); // 46
Run Code Online (Sandbox Code Playgroud)