Note that there are some explanatory texts on larger screens.

plurals
  1. POImplement universal, lightweight, and unobtrusive tagging of arbitrary objects?
    primarykey
    data
    text
    <p><strong>NB:</strong>&nbsp;<em>The material in the subsection titled</em>&nbsp;<strong>Background</strong>&nbsp;<em>is not essential. The full description of the question is fully contained in the preceding paragraphs.</em></p> <p>I'd like to implement a universal, lightweight, and "unobtrusive" way to "tag" arbitrary objects.</p> <p>More specifically, I want to define the equivalent of the (abstract) functions <code>tag</code>, <code>isTagged</code>, and <code>getTagged</code>, such that:</p> <ol> <li><code>isTagged(t)</code> is <code>true</code> if and only if <code>t</code> was the value returned by <code>tag(o)</code>, for some object <code>o</code>;</li> <li><code>getTagged(tag(o))</code> is identical to <code>o</code>, for every object <code>o</code>;</li> <li>if <code>t = tag(o)</code>, then <code>tag(t)</code> should be identical to <code>t</code>;</li> <li>with the exception of the behaviors described in (1), (2), and (3) above, and strict identity tests involving <code>===</code>, <code>tag(o)</code> and <code>o</code> should behave the same way.</li> </ol> <p>[EDIT: One further requirement is that the implementation should not modify the <code>Object</code> class, nor any other standard class, in any way.]</p> <p>For example:</p> <pre><code>&gt;&gt;&gt; isTagged(o = "foo") false &gt;&gt;&gt; isTagged(t = tag(o)) true &gt;&gt;&gt; getTagged(t) === o true &gt;&gt;&gt; tag(t) === t true &gt;&gt;&gt; t.length 3 &gt;&gt;&gt; t.toUpperCase() "FOO" </code></pre> <p>Below I give my best shot at solving this problem. It is (almost) universal, but, as it will soon be clear, it is <strong>anything but</strong> lightweight!!! (Also, it falls rather short of fully satisfying requirement 4 above, so it is not as "unobtrusive" as I'd like. Moreover, I have serious doubts as to its "semantic correctness".)</p> <p>This solution consists of wrapping the object <code>o</code> to be tagged with a "proxy object" <code>p</code>, and copying <strong>all</strong> the properties of <code>o</code> (whether "owned" or "inherited") to <code>p</code>.</p> <p>My question is:</p> <blockquote> <p>is it possible to achieve the specifications given above without having to copy all the properties of the tagged object?</p> </blockquote> <hr> <h2>Background</h2> <p>Here's the implementation alluded to above. It relies on the utility function <code>getProperties</code>, whose definition (FWIW) is given at the very end.</p> <pre><code>function Proxy (o) { this.__obj = o } function isTagged(t) { return t instanceof Proxy; } function getTagged(t) { return t.__obj; } var tag = (function () { function _proxy_property(o, pr) { return (typeof pr === "function") ? function () { return pr.apply(o, arguments) } : pr; } return function (o) { if (isTagged(o)) return o; if (typeof o.__obj !== "undefined") { throw TypeError('object cannot be proxied ' + '(already has an "__obj" property)'); } var proxy = new Proxy(o); var props = getProperties(o); // definition of getProperties given below for (var i = 0; i &lt; props.length; ++i) { proxy[props[i]] = _proxy_property(o, o[props[i]]); } return proxy; } })(); </code></pre> <p>This approach, ham-fisted though it is, at least seems to work:</p> <pre><code>// requirement 1 &gt;&gt;&gt; isTagged(o = "foo") false &gt;&gt;&gt; isTagged(p = tag(o)) true // requirement 2 &gt;&gt;&gt; getTagged(p) === o true // requirement 3 &gt;&gt;&gt; tag(p) === p true // requirement 4 &gt;&gt;&gt; p.length 3 &gt;&gt;&gt; p.toUpperCase() "FOO" </code></pre> <p>...well, <em>almost</em>; requirement (4) is not always satisfied:</p> <pre><code>&gt;&gt;&gt; o == "foo" true &gt;&gt;&gt; p == "foo" false &gt;&gt;&gt; o == o true &gt;&gt;&gt; p == o false </code></pre> <hr> <p><strong>FWIW</strong>, here's the definition of the function <code>getProperties</code>, which is used by the <code>tag</code> function. Criticisms welcome. (<strong>WARNING:</strong> <em><strong>I'm a completely clueless JS noob who doesn't know what he's doing!</em></strong> Use this function at your own risk!)</p> <pre><code>function getProperties(o) { var seen = {}; function _properties(obj) { var ret = []; if (obj === null) { return ret; } try { var ps = Object.getOwnPropertyNames(obj); } catch (e if e instanceof TypeError &amp;&amp; e.message === "obj is not an object") { return _properties(obj.constructor); } for (var i = 0; i &lt; ps.length; ++i) { if (typeof seen[ps[i]] === "undefined") { ret.push(ps[i]); seen[ps[i]] = true; } } return ret.concat(_properties(Object.getPrototypeOf(obj))); } return _properties(o); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload