️ remove object spread operator for faster performance in big array iterator (#2812)

This commit is contained in:
Kuitos 2023-11-15 16:19:33 +08:00 committed by GitHub
parent eff4306401
commit e75952faed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 18 deletions

View File

@ -70,10 +70,15 @@ export function rebindTarget2Fn(target: any, fn: any): any {
'toString',
);
Object.defineProperty(boundValue, 'toString', {
...originToStringDescriptor,
...(originToStringDescriptor?.get ? null : { value: () => fn.toString() }),
});
Object.defineProperty(
boundValue,
'toString',
Object.assign(
{},
originToStringDescriptor,
originToStringDescriptor?.get ? null : { value: () => fn.toString() },
),
);
}
}

View File

@ -24,9 +24,27 @@ function uniq(array: Array<string | symbol>) {
}, Object.create(null));
}
const cachedGlobalsInBrowser = globalsInBrowser
.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : [])
.reduce<Record<string, true>>((acc, key) => ({ ...acc, [key]: true }), Object.create(null));
/**
* transform array to object to enable faster element check with in operator
* @param array
*/
function array2TruthyObject(array: string[]): Record<string, true> {
return array.reduce(
(acc, key) => {
acc[key] = true;
return acc;
},
// Notes that babel will transpile spread operator to Object.assign({}, ...args), which will keep the prototype of Object in merged object,
// while this result used as Symbol.unscopables, it will make properties in Object.prototype always be escaped from proxy sandbox as unscopables check will look up prototype chain as well,
// such as hasOwnProperty, toString, valueOf, etc.
// so we should use Object.create(null) to create a pure object without prototype chain here.
Object.create(null),
);
}
const cachedGlobalsInBrowser = array2TruthyObject(
globalsInBrowser.concat(process.env.NODE_ENV === 'test' ? ['mockNativeWindowFunction'] : []),
);
function isNativeGlobalProp(prop: string): boolean {
return prop in cachedGlobalsInBrowser;
}
@ -70,22 +88,14 @@ export const cachedGlobals = Array.from(
),
);
// transform cachedGlobals to object for faster element check
const cachedGlobalObjects = cachedGlobals.reduce((acc, globalProp) => ({ ...acc, [globalProp]: true }), {});
const cachedGlobalObjects = array2TruthyObject(cachedGlobals);
/*
Variables who are impossible to be overwritten need to be escaped from proxy sandbox for performance reasons.
But overwritten globals must not be escaped, otherwise they will be leaked to the global scope.
see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables
*/
const unscopables = without(cachedGlobals, ...accessingSpiedGlobals.concat(overwrittenGlobals)).reduce(
(acc, key) => ({ ...acc, [key]: true }),
// Notes that babel will transpile spread operator to Object.assign({}, ...args), which will keep the prototype of Object in merged object,
// while this result used as Symbol.unscopables, it will make properties in Object.prototype always be escaped from proxy sandbox as unscopables check will look up prototype chain as well,
// such as hasOwnProperty, toString, valueOf, etc.
// so we should use Object.create(null) to create a pure object without prototype chain here.
Object.create(null),
);
const unscopables = array2TruthyObject(without(cachedGlobals, ...accessingSpiedGlobals.concat(overwrittenGlobals)));
const useNativeWindowForBindingsProps = new Map<PropertyKey, boolean>([
['fetch', true],
@ -93,7 +103,7 @@ const useNativeWindowForBindingsProps = new Map<PropertyKey, boolean>([
]);
function createFakeWindow(globalContext: Window, speedy: boolean) {
// map always has the fastest performance in has check scenario
// map always has the fastest performance in has checked scenario
// see https://jsperf.com/array-indexof-vs-set-has/23
const propertiesWithGetter = new Map<PropertyKey, boolean>();
const fakeWindow = {} as FakeWindow;