Created
August 9, 2019 05:13
-
-
Save bmeck/3955e617be395094432f4a3ceab23de7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
/** | |
* @template {Object} O | |
* @constructor | |
* @param {O} o | |
* @returns {O} | |
*/ | |
function Override(o) { | |
return o; | |
} | |
delete Override.constructor; | |
/** | |
* Used to create a private entity for storing data. | |
* Allows data to be stored directly on Objects and | |
* not using WeakMaps. | |
* @template {Object} O | |
* @template {any} V | |
*/ | |
function Private() { | |
class PrivateDeclaration extends Override { | |
#data; | |
/** | |
* @param {O} o | |
* @param {V} v | |
*/ | |
constructor(o, v) { | |
super(o); | |
// @ts-ignore | |
if (o !== this) { | |
throw new Error('Cannot install private data on a primitive'); | |
} | |
o.#data = v; | |
} | |
setup() { | |
/** | |
* @param {O} o | |
* @param {V} v | |
* @returns {V} | |
*/ | |
const insert = (o, v) => { | |
new PrivateDeclaration(o, v); | |
return v; | |
}; | |
/** | |
* @param {O} o | |
* @param {() => V} insert | |
* @param {(v: V) => V} update | |
* @returns {V} | |
*/ | |
const insertOrUpdate = (o, alloc = () => void 0, update = v => v) => { | |
let v; | |
try { | |
const old = get(o); | |
set(o, v = update(old)); | |
} catch (e) { | |
insert(o, v = alloc()); | |
} | |
return v; | |
} | |
/** | |
* @param {O} o | |
* @param {V} v | |
* @returns {V} | |
*/ | |
const set = (o, v) => { | |
return o.#data = v; | |
}; | |
/** | |
* @param {O} o | |
* @returns {V} | |
*/ | |
const get = (o) => { | |
return o.#data; | |
} | |
/** | |
* @param {O} o | |
* @param {V} otherwise | |
* @returns {V} | |
*/ | |
const getOr = (o, otherwise) => { | |
try { | |
return o.#data; | |
} catch { | |
} | |
return otherwise; | |
} | |
/** | |
* @param {O} o | |
* @returns {boolean} | |
*/ | |
const has = (o) => { | |
try { | |
o.#data; | |
return true; | |
} catch { | |
} | |
return false; | |
}; | |
return Object.freeze({ | |
__proto__: null, | |
insert, | |
insertOrUpdate, | |
set, | |
get, | |
getOr, | |
has | |
}); | |
} | |
}; | |
delete PrivateDeclaration.constructor; | |
Reflect.setPrototypeOf( | |
PrivateDeclaration.prototype, | |
null | |
); | |
/** | |
* @type {typeof fake} | |
*/ | |
const ret = PrivateDeclaration.prototype.setup(); | |
return ret; | |
// the next line is wonky to make type inference happy | |
const fake = new PrivateDeclaration(/** @type {O} */({}), null).setup(); | |
} | |
module.exports = Private; |
@hax that assumes I wanted to compare all objects I attach data to against a class (which is multiple fields). The point of this is exactly that I am attaching data, not a class to arbitrary objects. It doesn't make sense to state that o of class Fake
is true if it isn't a Fake
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@bmeck I still not understand how this have difference with weakmap version in GC.
And assume the alternative syntax keep the same semantic, the
try
hack could be replaced by#data in o
oro of class
orclass.checkBrand(o)
without any difference.