The following are comments I sent Allen on a slightly earlier draft. I think all these are still applicable.<br><br><div class="gmail_quote">---------- Forwarded message ----------<br>From: <b class="gmail_sendername">Mark S. Miller</b> &lt;<a href="mailto:erights@google.com">erights@google.com</a>&gt;<br>
Date: Tue, Apr 22, 2008 at 4:32 PM<br>Subject: Re: define/getProperty meta operations<br>To: Allen Wirfs-Brock &lt;<a href="mailto:Allen.Wirfs-Brock@microsoft.com">Allen.Wirfs-Brock@microsoft.com</a>&gt;<br><br><br>








<blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US">

<div>

<p><b>Problem:&nbsp; Design Pattern for Supporting Meta
Operations Upon Objects&nbsp; -- Design Sketch &amp; Notes</b></p>

<p><b>Not Yet Spec. or Proposal</b></p>

<p><b>Allen Wirfs-Brock</b></p>

<p><b>&nbsp;</b></p>

<p>ECMAScript needs to support an enhanced API for manipulating
and query the structural and behavior definition of dynamic objects. Example of
such operations include the definition of setter/getter properties and the
setting of property attributes such as [[dontenum]].&nbsp; A single design pattern
needs to be established to guide the design of these new functions.</p>

<p><b>General Approach</b></p>

<p>Add new static functions as ReadOnly, DontDelete properties
of Object constructor. Parameterize with literal arguments enable possible analysis
and static implementation of complex objects. Minimize number of entry points using
complex patterns of optional arguments. Use JSON-style object-literal to
implement object pattern.&nbsp; Use same object pattern for both property
definition/query.</p></div></div></blockquote><div><br>Hi Allen,<br><br>As discussed on the phone, I like a lot of this! However, I would like to use a nested object literal to describe a set of properties, and I want to avoid using a function&#39;s declared name as a way to cutely specify the corresponding property name. Below each of your use cases, I&#39;ll show the alternative I have in mind.<br>

<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p></p>

<p><b>Define/Modify a property and/or property attributes</b></p>

<p>Object.defineProperty(obj, descriptor)</p></div></div></blockquote><div>Object.defineProperties(&lt;obj&gt;, &lt;multiDescriptor&gt;)<br><br>&lt;multiDescriptor&gt; = { (&lt;propertyName&gt;: &lt;descriptor&gt;)* }<br>

&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p></p>

<p>Add to <i>obj</i> the property described by <i>descriptor.</i>
Set properties of the attribute as described by <i>descriptor</i>.&nbsp; Return <i>false</i>
if is not successful.</p></div></div></blockquote><div>throws if not successful.<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p></p>

<p><b>Use case: modify attributes of an existing property.</b></p>

<p>Descriptor = { name: <b>string</b><i>, </i>[, writable: <b>boolean</b>]
[, enumerable: <b>boolean</b>] [, deletable: <b>boolean</b>]}</p></div></div></blockquote><div>Without the &quot;name: string&quot; part, since that comes from the multiDescriptor.<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p></p>

<p>If <i>obj</i> has a local property whose name is the value
of the <i>name</i> element of the descriptor set the attributes of the property
according to the values of the writable, enumerable, and deletable elements of
the descriptor.&nbsp; Fail if the property does not exist &nbsp;(or add fixed/frozen attribute)</p></div></div></blockquote><div>If the property does not yet exist, then all unenumerated attributes of an explicitly defined property should default to false. If the property already exists, then unenumerated attributes should default to their existing value.<br>

&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p></p>

<p>Example:</p>

<p>Object.defineProperty(Array.prototype, {name: &quot;some&quot;,
enumerable: false});</p></div></div></blockquote><div>Object.defineProperties(Array.prototype, {some: {enumerable: false}});<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p></p>

<p><b>Use case: add a data property to an object.</b></p>

<p>Descriptor = { name: <b>string</b><i>, </i>value: <b>object </b>[,
writable: <b>true</b>] [, enumerable: <b>false</b>] [, deletable: <b>false</b>]}</p>

<p>Add to obj a property whose name is the value of the <i>name</i>
element of the descriptor and whose initial value &nbsp;is the value of the value
element of the descriptor. Set the attributes to the specified or default
values according to the elements of the descriptor.&nbsp; Fail if the property
already exists and is not writeable (or add fixed attribute)</p>

<p>Example:</p>

<p>var obj = new Object();</p>

<p>Object.addProperty(obj, {name: &quot;x&quot;, value: 0});</p>

<p>Object addProperty(obj, {name: &quot;y&quot;, value: 0});</p>



<p>Object.addProperty(obj, {name: &quot;pi&quot;, value: 3.14159,
writable: false});</p></div></div></blockquote><div><br>Object.defineProperties(obj, {x: {value: 0}, y: {value: 0}, {pi: {writable: false}});<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div>

<p> <b>Use case: add a method property to an object.</b></p>

<p>Descriptor = { name: <b>string</b><i>, </i>method: <b>function
</b>[, writable: <b>false</b>] [, enumerable: <b>false</b>] [, deletable: <b>false</b>]}</p>

<p>&nbsp;</p>

<p>Add to obj a property whose name is the value of the <i>name</i>
element of the descriptor and whose value &nbsp;is the value of the method element
of the descriptor. Set the attributes of the specified or default values
according to the elements of the descriptor.&nbsp; Fail if the property already
exists and is not writeable (or add fixed attribute) or if the value of the <i>method</i>
element is not a function.</p>

<p>Notes:&nbsp; The primary difference from the data property use case
is that the writable attribute defaults to false rather than true.</p></div></div></blockquote><div>Unenumerated attributes of newly defined properties should always default to false. Are there other reasons to distinguish methods from values? I will proceed for now assuming not.<br>

<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p></p>

<p>Examples:</p>

<p>Object.addProperty(obj, {name: doSomeWork, method: function
() {this.work()});</p>

<p>Aternative form assuming existence of
<a href="http://Function.prototype.name" target="_blank">Function.prototype.name</a>:</p>

<p>Object.addProperty(obj, {method: function doSomeWork ()
{this.work()});</p></div></div></blockquote><div>Object.defineProperties(obj, {doSomeWork: {value: function() {this.work();}}});<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p></p>

<p><b>Use case: add a getter/setter property to an object.</b></p>

<p>Descriptor = { name: <b>string</b><i>, </i>getter: <b>function
</b>[, writable: <b>false</b>] [, enumerable: <b>false</b>] [, deletable: <b>false</b>]}</p>

<p>Or</p>

<p>Descriptor = {name: <b>string</b><i>, </i>getter: <b>function</b>,
setter:&nbsp; <b>function</b> </p>





<p style="margin-left: 0.5in; text-indent: 0.5in;">[, writable: <b>true</b>]
[, enumerable: <b>false</b>] [, deletable: <b>false</b>]}</p><p>&nbsp;</p></div></div></blockquote><div>But without the &quot;name: string&quot; part again.&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p><br>Add to obj a property getter (and setter) whose name is the
value of the <i>name</i> element of the descriptor and whose value &nbsp;is the
value of the <i>getter</i> (and <i>setter</i>) element(s) of the descriptor. Set
the attributes of the specified or default values according to the elements of
the descriptor.&nbsp; Fail if the property already exists and is not writeable (or
add fixed attribute) or if the value of the <i>getter </i>or<i> setter</i>
element is not a function.</p>

<p>Examples:</p>

<p>var privateprop_y = new Object();</p>

<p>var obj = new Object();</p>

<p>Object.addProperty(obj, {name: privateprop_y, value: 0});</p>

<p>Object addProperty(obj, {name: &quot;y&quot;, </p>

<p style="margin-left: 1in; text-indent: 0.5in;">getter: function
() {return this[privateprop]},</p>

<p style="margin-left: 1in; text-indent: 0.5in;">setter:
function (arg) {this[privateprop] = arg} &nbsp;});</p></div></div></blockquote><div><br>Whoa! In ES3, all property names are strings. Above, you seem to suggest allowing first class anon object refs as keys. This is eventually attractive on many grounds, but is *way* too big a step for ES3.1. Below, I will assume instead the property name &quot;privateprop_y&quot;, which, of course, isn&#39;t private at all.<br>

&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p style="margin-left: 1in; text-indent: 0.5in;"></p><p>Alternative form assuming existence of
<a href="http://Function.prototype.name" target="_blank">Function.prototype.name</a>:</p>

<p>Object addProperty(obj, { getter: function y() {return
this[privateprop]},</p>

<p style="margin-left: 1in; text-indent: 0.5in;">&nbsp;&nbsp; setter:
function y(arg) {this[privateprop] = arg} &nbsp;});</p>

</div></div></blockquote><div><br>Object.defineProperties(obj, {<br>&nbsp;&nbsp;&nbsp; privateprop_y: {value: 0, writable: true},<br>&nbsp;&nbsp;&nbsp; y: {getter: function() {return this.privateprop_y;},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setter: function(arg) {this.privateprop_y = arg;}}<br>

});<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div>

<p><b>Query existence and attributes of an object&#39;s properties</b></p></div></div></blockquote><div>Object.getProperties(obj) =&gt; &lt;multiDescriptor&gt;<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p><b> </b></p>

<p>Object.getProperty(obj, name)</p>

<p>If <i>obj</i> has a locally defined property whose name is <i>name</i>
return a fully populated proper descriptor object as used with
Object.defineProperty.&nbsp; Return <i>undefined</i> if the named property does not
exist.</p></div></div></blockquote><div>As if defined by<br><br>Object.getProperty = function(obj, name) { return Object.getProperties(obj)[name]; };<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">

<div lang="EN-US"><div><p></p>

<p><b>Other possible object constructor functions:</b></p>

<p>Object.freeze(obj)</p></div></div></blockquote><div>as well as Object.seal(obj) which seals without freezing. A sealed object is non-dynamic -- new properties cannot be added to it, and its existing properties become {deletable: false}. A frozen object is a sealed object with the additional constraint that all its existing properties become {writable: false}. <br>

</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote"><div lang="EN-US"><div><p></p>

<p>Object.defineProperties(obj, {[ {<i>descriptor</i>},
propName2: {<i>descriptor</i>},])</p>

<p>Object.getProperties(obj)</p>

<p>Crock&#39;s beget function, etc.</p>

<p>Create object with properties</p>

<p>&nbsp;</p>

<p><b>Sketch:&nbsp; Defining a &quot;fixture&quot;</b></p>

<p>var fixture = new Object();</p>

<p>Object.defineProperty(fixture, {method: function method1
() {}});</p>

<p>Object.defineProperty(fixture, {method: function method2
() {}});</p>

<p>Object.defineProperty(fixture, {getter: function prop3 ()
{}, setter: function (arg) {}});</p>

<p></p>

<p>Object.freeze(proto)</p></div></div></blockquote><div>var fixture = new Object();<br>Object.defineProperties(fixture, {<br>&nbsp;&nbsp;&nbsp; method1: {value: function(...){...}},<br>&nbsp;&nbsp;&nbsp; method2: {value: function(...){...}},<br>&nbsp;&nbsp;&nbsp; prop3: {getter: function(){...}, setter: function(arg){...}}<br>

});<br>Object.seal(fixture); // for the last line above, is this what you meant?<br>&nbsp;</div><blockquote style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;" class="gmail_quote">
<div lang="EN-US">
<div><p></p><p>var template = new Object();</p>

<p>Object.defineProperty(template, {name: &quot;var1&quot;, value:
undefined});</p>

<p>Object.defineProperty(template, {name: &quot;var2&quot;, value:
undefined});</p>

<p></p>

<p>Object.freeze(template);</p>

<p></p>

<p>return Object.beget(proto,template);&nbsp; //new object with
properties form template and prototype proto</p></div></div></blockquote><div>I still don&#39;t like the idea of extending beget with copying behavior.<br><br><br><br></div>
</div><br><br clear="all"><br>-- <br> &nbsp; &nbsp;Cheers,<br> &nbsp; &nbsp;--MarkM