data1 = {};
handler = {
get: (target, prop) => (target[prop]),
set: (target, prop, value) => (target[prop] = value),
};
proxy = new Proxy(data1, handler);
data2 = {
get test() {
return this._test;
},
set test(value) {
return this._test = value;
}
};
const EVENTS = new WeakMap();
function pub(object, event, data) {
if (!EVENTS.has(object)) { return; }
const callbacks = EVENTS.get(object)[event];
if (callbacks) {
for (const callback of callbacks) {
callback(data);
}
}
}
function sub(object, event, callback) {
let dict;
if (!EVENTS.has(object)) {
EVENTS.set(object, dict={});
} else {
dict = EVENTS.get(object);
}
let evList;
if (event in dict) {
(evList = dict[event]).push(callback);
} else {
evList = dict[event] = [callback];
}
return function unsub() {
evList.splice(evList.indexOf(callback), 1);
}
}
data3 = {};
sub(data3, 'set-test', v => data3.test = v);
sub(data3, 'get-test', () => pub(data3, 'getting-test', data3.test));
sub(data3, 'getting-test', v => a += v);
var a = 0;
data4 = {};
data5 = {};
sub(data5, 'set-test', v => { data5.test = v; pub(data5, 'getting-test', data3.test); });
sub(data5, 'getting-test', v => a += v);
data2.test = Math.random();
a += data2.test;
proxy.test = Math.random();
a += proxy.test;
pub(data3, 'set-test', Math.random());
pub(data3, 'get-test');
data4.test = Math.random();
a += data4.test;
// note that is almost certainly not accurate since it probably ends after this function is called, not when getting-test triggers
pub(data3, 'set-test', Math.random());
--enable-precise-memory-info
flag.
Test case name | Result |
---|---|
getter / setter | |
proxy | |
events | |
plain object | |
events (kinda cheating) |
Test name | Executions per second |
---|---|
getter / setter | 378371552.0 Ops/sec |
proxy | 17582520.0 Ops/sec |
events | 8451159.0 Ops/sec |
plain object | 428359424.0 Ops/sec |
events (kinda cheating) | 23594872.0 Ops/sec |
Let's break down the benchmark and explain what's being tested, along with their pros and cons.
Benchmark Overview
The benchmark measures the performance of three different approaches to updating a value in an object:
Each test case has a unique variation, which we'll discuss below.
Options Compared
Here are the options being compared, along with their pros and cons:
The getter/setter method uses JavaScript's built-in get
and set
methods to update the value. This approach is fast but may incur additional overhead due to the internal workings of these methods.
The Proxy API creates a new proxy object that intercepts and modifies values before they are updated. This approach provides more control over updates but can be slower due to the additional overhead.
The Events approach uses a WeakMap to store event listeners, which allows for lightweight and efficient updates. However, this approach may incur additional memory usage due to the storage of these listeners.
Library and Special JS Feature
In this benchmark, we don't see any specific libraries being used beyond JavaScript's built-in features (e.g., Proxy API). There are no special JS features being tested; however, some nuances of the Events approach require a basic understanding of WeakMap and event handling in JavaScript.
Other Alternatives
To compare with these approaches, developers might consider other alternatives:
Keep in mind that these alternatives depend on the specific requirements and constraints of the project, as well as the chosen programming paradigm and technology stack.