From 1301e5b0afeee91d0f2dacb41ea9f9dd899d1cdf Mon Sep 17 00:00:00 2001 From: Kuitos Date: Thu, 29 Jul 2021 10:21:44 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20function=20should=20return=20the?= =?UTF-8?q?=20same=20reference=20while=20it=20always=20bindable=20(#1612)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sandbox/__tests__/common.test.ts | 2 ++ src/sandbox/common.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/sandbox/__tests__/common.test.ts b/src/sandbox/__tests__/common.test.ts index 4030a9d..fe5e290 100644 --- a/src/sandbox/__tests__/common.test.ts +++ b/src/sandbox/__tests__/common.test.ts @@ -39,6 +39,8 @@ describe('getTargetValue', () => { expect(result2).toStrictEqual({ field: '456' }); // window.field not be affected expect(window.field).toEqual('123'); + // reference should be stable after first running + expect(constructableFunction).toBe(getTargetValue(window, prototypeAddedAfterFirstInvocation)); }); it('should work well while value have a readonly prototype on its prototype chain', () => { diff --git a/src/sandbox/common.ts b/src/sandbox/common.ts index f936f29..6a1fd21 100644 --- a/src/sandbox/common.ts +++ b/src/sandbox/common.ts @@ -14,6 +14,7 @@ export function setCurrentRunningSandboxProxy(proxy: WindowProxy | null) { currentRunningSandboxProxy = proxy; } +const functionBoundedValueMap = new WeakMap(); export function getTargetValue(target: any, value: any): any { /* 仅绑定 isCallable && !isBoundedFunction && !isConstructable 的函数对象,如 window.console、window.atob 这类,不然微应用中调用时会抛出 Illegal invocation 异常 @@ -21,6 +22,11 @@ export function getTargetValue(target: any, value: any): any { @warning 这里不要随意替换成别的判断方式,因为可能触发一些 edge case(比如在 lodash.isFunction 在 iframe 上下文中可能由于调用了 top window 对象触发的安全异常) */ if (isCallable(value) && !isBoundedFunction(value) && !isConstructable(value)) { + const cachedBoundFunction = functionBoundedValueMap.get(value); + if (cachedBoundFunction) { + return cachedBoundFunction; + } + const boundValue = Function.prototype.bind.call(value, target); // some callable function has custom fields, we need to copy the enumerable props to boundValue. such as moment function. @@ -40,6 +46,7 @@ export function getTargetValue(target: any, value: any): any { Object.defineProperty(boundValue, 'prototype', { value: value.prototype, enumerable: false, writable: true }); } + functionBoundedValueMap.set(value, boundValue); return boundValue; }