A bit on Object.extend(obj,{...}) vs. obj.{...}

To get started, just a reminder that we can't really compare the two based on 
the assumption that the second argument to extend is an object literal as there 
is no way to syntactically guarantee that.  We have to assume that both 
arguments to Object.extend are preexisting object that were created in some 
arbitrary manner.  EG, we are have to compare Object.extend(obj1, obj2) vs. 
obj1.{...}


On Apr 29, 2012, at 10:10 AM, Axel Rauschmayer wrote:

> ...
> Object.extend would automatically invoke Object.defineMethod() (or similar) 
> for you, to enable super-references (but I doubt there is much use for them 
> in mixins).

Mixin's and inheritance (including) super references are orthogonal concepts.  
Some may choose to use only one or the other but they really need to be 
composable for for those who need to use them in combination. 

The defineMethod point is important.  Equally important is the treatment of 
private named properties.

To correctly preserve super invocation semantics methods must be processed 
using Object.defineMethod when they are copied from obj2 to obj1.   For 
obj1.{...} this isn't a problem because the "extension object" and its 
properties don't preexist.  They are  just syntax that is processed just like 
creating completely new properties on obj1 and the semantic rules for those new 
properties are exactly the same as those that would have been used in an object 
literal that was providing the initial definition of obj1.  There is no copying 
semantics of worry about.

One of the syntactic distinctions that is made by obj1.{...}  is the difference 
between a "method" property and a function valued "state" property.  Consider:

//somewhere else in the program
vender.getSomeCallback = function(a,b) {
        let cb=  function() 
{...myWeakMap.get(cb).attachment...this.something(a,b)....};
        myWeakMap.set(cb, generateAttachment(a,b));
        return cb;
}

//main example
obj1.{
   callback: vender.getSomeCallback(x,y),
   meth() {
       super.meth();
       this.callback.call(this.target);
 }
   
Note that .{ sees the callback property as a regular "state" property and 
doesn't use defineMethod semantics to create  it on obj1. The value returned by 
vender.getSomeCallback is directly used as the value of the callback state 
property and the identify based annotation mechanism used in the definition of 
the callback will work just fine.  meth, because it is expressed using concise 
method syntax is processed as a new method definition that is super bound as if 
by defineMethod to obj1.

Now consider using extend:

let obj2 = {
   callback: vender.getSomeCallback(x,y),
   meth() {
       super.meth();
       this.callback.call(this.target);
 };

Object.extend(obj1,obj2);

Object.extend doesn't have any visibility of how the properties of obj2 were 
defined. All it sees is two data properties whose values are functions.  It if 
used Object.defineProperty to process both properties then methwould be super 
bound to the wrong object. If it used Object.defineMethod on both properties 
then the callback function's identify may change.  What Object.extend will have 
to do is have a heuristic (if the value is  super bound to obj2, then 
defineMethod it to obj1.  Otherwise, just use defineProperty) that it applies 
to function valued data properties.   This heuristic  should be adequate for 
uses that correspond to what could be expressed using .{ but it also allows 
other cases where the heuristic may produce something other than the desired 
result.  Those situations simply don't exist for .{

Use of private names with Object.extend is even more problematic. 

Consider something like:

let MixinArrayIterator = (obj) => obj.{
   @iterator() {
          let coll = this;
          return function*() {...yield coll[...}
    }
}
       
which uses the built-in @iterator private name.  This works nicely with  .{ 
because the private name is used within the .{ special form and doesn't require 
any runtime reflection on the mixin "object".  However, to do the same with 
Object.extend would requires that extend has reflective visibility of actual 
private named properties of obj2. The reflection restrictions on private names 
exists to support various high-integrity use cases.  In this particular 
situation, there is nothing actually "private" in the hight-integrity sense 
about @iterator.  To make this all work with extend we would probably have to 
re-introduce  the concept of "unique names" which are like private names but 
without the reflection restrictions. That then would introduces additional 
complexities.

It is probably possible to support mixin attachment using a  functional form 
(Object.extend) rather than using a special form (.{} ) but there is a lot more 
to it than just a simply syntactic substitutions. 

Allen

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to