Hello I am wondering how to write a proper wrapper for something like sessionStorage
.
I could implement a class of my own and proxy through method invocation to the sessionStorage
. For example with some quick pseudocode:
class customStorage {
set(key, value) {
sessionStorage.setItem(key, value);
}
}
I would use it like so:
const customStorage = new customStorage();
customStorage.set('my-key', 'hello there');
All fine and dandy but I would like for a user to have the freedom to use other native sessionStorage
methods on my proxy that I might not implement myself in my proxy.
for something like sessionStorage
it would be do-able to write them all yourself even though all they might do is proxy through to sessionStorage
without any intervention.
For something larger where I would only manipulate 5 methods out of 20 or something this does not seem viable.
Overwriting native functionality with prototype also seems like a deathtrap leading to many wtfs-per-minute
.
So far what I have read from 'proxy patterns' in Javascript they all implemented all of the methods from the original Object. Am I forced to do this?
Is there some sort of way to create an ES6 class and set the prototype of that very class to sessionStorage in the constructor or something?
I would like for a user to have the freedom to use other native
sessionStorage
methods on my proxy that I might not implement myself in my proxy.
I would rather give the user the freedom to just use the native sessionStorage
directly if he intends to do that. Your implementation does have its own, separate functionality, which does use sessionStorage
internally but is not the sessionStorage
. There's no reason to implement its interface on your object. (See also composition over inheritance).
Is there some sort of way to create an ES6
class
and set the prototype of that very class tosessionStorage
in the constructor or something?
No. Even when you want to implement that interface, your objects are not really SessionStorage
instances. Also in this particular case, sessionStorage
is a singleton and you cannot instantiate a second SessionStorage
, so inheritance does absolutely not work here.
There are three ways around this (I'll write code for the generic case with instantiation from arbitrary objects to be wrapped, you likely want a static singleton-like one for your custom storage):
Mixins to decorate the object. Don't create another instance, just overwrite the properties on the original. (This is likely out of the question for builtin objects)
function custom(orig) {
orig.get = function() { … };
return orig;
}
Parasitical inheritance, creating a complete wrapper using reflection on the object.
function custom(orig) {
const obj = {
get() { … };
};
for (const p in orig) { // assuming everything is enumerable - alternatively use
// for (const p of Object.getOwnPropertyNames(…))
// or even incorporating the prototype chain
obj[p] = typeof orig[p] == "function"
? (...args) => orig[p](...args)
: orig[p];
}
return obj;
}
A literal Proxy
with a suitable handler:
const customMethods = {
get() { … }
};
const handler = {
has(target, name) {
return name in customMethods || name in target;
},
get(target, name) {
if (name in customMethods) return customMethods[name];
else return target[name];
// if its a native object with methods that rely on `this`, you'll need to
// return target[name].bind(target)
// for function properties
}
}
function custom(orig) {
return new Proxy(orig, handler);
}