Object.extends (was Re: traits feedback)

# John J Barton (14 years ago)

On Thu, Oct 6, 2011 at 8:04 PM, Andrew Dupont <mozilla at andrewdupont.net>wrote:

(Keep in mind that the 80% use-case for this sort of thing is merging default options with user-supplied options, at least in the code I write. That's a simple case that usually involves merging two plain objects without interesting proto values. That's not to say that we should ignore the edge cases; but let's just keep them in the proper perspective, and not let them hold up progress on this front.)

While I agree with Andrew, I wonder if the issue really edge cases. Rather I guess the problem lies in different vision. Object.extend is a bottom up request for functionality; language experts may object that this function is not needed in a proper class-based language?

If all the arguments to Object.extend() have prototype Object, then I guess the function is clear.

If any of the arguments have other prototypes, we have cases and choices. for..in: all the properties are merged and the final object has prototype Object keys: only the own properties are merged and the final object has prototype Object. proto-power: non-function own properties are merged; function properties create prototype chain, left to right.

I suppose all the above is also possible: for..in: Object.flattenProperties() returned a simple object of the for..in kind. keys: Object.ownProperties() returned a simple object of the own properties of the arguments, proto-power: Object.extend()

If Object.create() were upgraded to allow a simple object on the second argument (so the result of the above operations can be passed in), then the base Object functions would do a lot of what devs do now with loops over properties.

jjb

# John-David Dalton (14 years ago)

From: Andrew Dupont <mozilla at andrewdupont.net> JJB has it mostly right. When Sam wrote Object.extend (which was originally Object.prototype.extend), it didn't use hasOwnProperty; my guess is that Sam didn't know about it, because most of us were JavaScript amateurs back in the day, including myself. When I joined the project over a year later, I remember discussing this problem and discovering that Safari 2.0 didn't support hasOwnProperty.

For an example of creating a workaround for lack of hasOwnProperty see: bestiejs/spotlight.js/blob/master/spotlight.js#L189

That, plus the hazard of changing implementation behavior, led us to leave things as they were. If I were writing Prototype's Object.extend from scratch, I'd definitely want it to copy "own" properties only.

MooTools also has an Object.extend() with a bonus that it tries to avoid IE bugs with for..in over shadowed Object.prototype properties. I've used similar private extend() functions that mimic the Prototype/MooTools behavior and remember in at least one of my projects running into problems when attempting to make extend() only iterate over own properties.

Anyway, I'm in favor of standardizing Object.extend in ES6, but in a way that considers only "own" properties (including private names, if so desired). I don't think ES6 should copy the exact behavior of Prototype's (flawed) version of Object.extend. Whether that takes the form of a method named "Object.extend" or a .{ operator? well, that's less important to me, but my vote is for the former.

The former, Object.extend, will be hard to add if it is to only iterate over own properties, as I think it should, because Prototype/MooTools and others add their own Object.extend, without checking if the method exists, paving any existing method with their own implementation. This will create inconsistencies between pages/third-party code that use Prototype/MooTools and those that don't. Introducing an operator has the advantage of not being overwritten/mucked-with and allows for shorter/fluid syntax. I <3 monocle operator!

# John-David Dalton (14 years ago)

On Fri, Oct 7, 2011 at 10:17 AM, Jake Verbaten <raynos2 at gmail.com> wrote:

Object.extend is the only method I shim on every es5 project, so +1

However arguments like library X is would break are stupid, remember es6 is an opt-in so backwards compat with existing code be damned.

No U! My concern came from seeing ES5 additions like Function#bind partially hindered because spec'ed behavior isn't covered by most shims/existing lib implementations. For example using the bound function's behavior with the new operator is inconsistent with most third party code/shims. But since it's opt-in yes you can muck with dev expectations and change the behavior of additions that have existed for 6yrs or so :D

As for the functionality, it should just be an n-ary function which extends the first object with all the own properties of the other n-1 objects. If keys clash give right precedence.

I've used this flavor of extends too (minus the own properties part).

whether key clashes favour left or right isnt important. whether the return value is undefined or the first object isnt important.

Yap but chaining FTW.

please dont try arguments from authority like bad library X copies over enumerable properties from the prototype chain so we should too.

If that part is opt-in then sure go for implementing it better than existing libs but why not push for the monocle operator?

# Quildreen Motta (14 years ago)

2011/10/7 John-David Dalton <john.david.dalton at gmail.com>

As for the functionality, it should just be an n-ary function which extends the first object with all the own properties of the other n-1 objects. If keys clash give right precedence.

I've used this flavor of extends too (minus the own properties part).

I also think this is the way to go. Object.extend should copy only own properties, though. Other use cases can be dealt with by creating a small abstraction to copy properties recursively. Or by providing a native .deepExtend. I'd assume the majority of the use-cases wouldn't want to deal with copying all descriptors -- taking into account we're not copying just enumerables here, it could well be a problem -- since it'd bring in too much overhead in each call.

whether key clashes favour left or right isnt important. whether the return value is undefined or the first object isnt important.

Yap but chaining FTW.

I think Object.extend should return the target object. It makes no sense to do otherwise -- considering the majority of JS native API either returns an useful value, and most of them are pure.

By the way, what are your thoughts on pure vs destructive Object.extend?

# John J Barton (14 years ago)

Several people advocate Object.extend() that copies only own properties. I don't understand why; I'll make the case for copying all properties.

At a call site I use, say foo.bar(); Ordinarily I should not be concerned about details of bar()'s implementation. In particular I should not be concerned if bar() is own or not.

Now I decide to create a new object from |foo|, by adding properties with Object.extend(): var fuz = Object.extend(foo, {paper:'in', shoes:'my'}); later I write fuz.bar();

If Object.extend() only uses own properties, the success or failure of that statement depends upon the location of bar() in foo. While one might argue that use of extend() is one case where devs should be concerned with details of bar()'s implementation, how does it help in this case? If bar() is in a prototype then I am back to manual property manipulation get the |fuz| object I want.

If Object.extend() copies all properties (into prototype or not is a different issue) then the resulting object acts as an extension of the one I want to extend.

On the other side I don't see any advantage to copying only own properties. Again I will suggest Object.getOwnProperties() as a way of providing the answer some would like to see. Again I will point out that Object.extend() with plain objects is not affected by these issues.

jjb

# Juan Ignacio Dopazo (14 years ago)

On Fri, Oct 7, 2011 at 12:34 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

   --

Dr. Axel Rauschmayer

I also think Object.extend suggests inheritance. Other possibilities would

be:

Object.merge() Object.mix() // Prior art: YUI's Y.mix()

There is also the question about whether this new function would overwrite already existing properties or not.

Juan

# Quildreen Motta (14 years ago)

2011/10/7 Juan Ignacio Dopazo <dopazo.juan at gmail.com>

On Fri, Oct 7, 2011 at 12:34 PM, Axel Rauschmayer <axel at rauschma.de> wrote:

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

   --

Dr. Axel Rauschmayer

I also think Object.extend suggests inheritance. Other possibilities would be:

Object.merge()

`Object.merge()' suggests a pure function though, which I don't think would be a bad thing at all.

# Axel Rauschmayer (14 years ago)

If you do something like var fuz = Object.extend(foo, {paper:'in', shoes:'my'});

Then fuz will get all properties of Object.prototype, again, as duplicates. In the above, you are clearly most interested in what you see in the literal and those are the own properties.

I don't understand how you can get properties as duplicates in JS.

Given the following: var target = Object.create(null); // target has not prototype console.log(toString in target); // false var source = { foo: 123 }; console.log(toString in source); // true

Object.extend(target, source); console.log(toString in target); // true

Object.extend would also add all of the following properties to target (copied from Object.prototype, which is the prototype of each object literal).

Object.getOwnPropertyNames(Object.getPrototypeOf({})) [ 'toString', 'lookupGetter', 'defineGetter', 'toLocaleString', 'hasOwnProperty', 'valueOf', 'defineSetter', 'constructor', 'propertyIsEnumerable', 'isPrototypeOf', 'lookupSetter' ]

Also note that JavaScript only changes properties in the first object in a prototype chain: var proto = { foo: 3 }; var obj = Object.create(proto); obj.foo = 5; console.log(proto.foo); // 3

Fine, but not germane as far as I can tell.

Loosely, it shows that own properties have always had special status in JavaScript (prototypes are there specifically to be shared).

And this kind of "extend" enables poor man’s cloning as follows: var orig = { foo: "abc" }; var clone = Object.extend(Object.create(Object.getPrototypeOf(orig)), orig);

Sadly this code will fail since -- surprise! -- Object.create() does not take an object as the second argument.

Object.create() only has a single argument above, it creates an empty object.

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

Since we live in a right to left world (a = b();) we need the target on the left. Then multiple RHS also works easily.

Swapping the parameters would is fine with me, then the name should probably be changed to something like copyOwnFrom(target, source).

However, note that if Object.create() were fixed: var clone = Object.create(Object.getPrototypeOf(orig), Object.getOwnProperties(orig));

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create(). But, in a way, the proto operator <| is that fix. However, your are on to something with the above which will work as follows in ES.next: var clone = Object.create(Object.getPrototypeOf(orig), Object.getOwnPropertyDescriptors(orig));

# John J Barton (14 years ago)

On Fri, Oct 7, 2011 at 9:23 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

If you do something like

 var fuz = Object.extend(foo, {paper:'in', shoes:'my'});

Then fuz will get all properties of Object.prototype, again, as duplicates. In the above, you are clearly most interested in what you see in the literal and those are the own properties.

I don't understand how you can get properties as duplicates in JS.

Given the following: var target = Object.create(null); // target has not prototype console.log(toString in target); // false var source = { foo: 123 }; console.log(toString in source); // true

Object.extend(target, source); console.log(toString in target); // true

Object.extend would also add all of the following properties to target (copied from Object.prototype, which is the prototype of each object literal).

Object.getOwnPropertyNames(Object.getPrototypeOf({})) [ 'toString', 'lookupGetter', 'defineGetter', 'toLocaleString', 'hasOwnProperty', 'valueOf', 'defineSetter', 'constructor', 'propertyIsEnumerable', 'isPrototypeOf', 'lookupSetter' ]

Sorry to be dense, but I still don't get it. How can an object have duplicate properties? I understand that own properties override properties on prototypes.

Also note that JavaScript only changes properties in the first object in a

prototype chain: var proto = { foo: 3 }; var obj = Object.create(proto); obj.foo = 5; console.log(proto.foo); // 3

Fine, but not germane as far as I can tell.

Loosely, it shows that own properties have always had special status in JavaScript (prototypes are there specifically to be shared).

Note this specialness is about writing, not reading. I'm fine with own properties having special status; I'm fine with Object.extend() treating own properties special. I'm not fine with Object.extend() that produces an object without some of the properties of the input arguments.

And this kind of "extend" enables poor man’s cloning as follows:

var orig = { foo: "abc" };
var clone = Object.extend(Object.create(Object.getPrototypeOf(orig)),

orig);

Sadly this code will fail since -- surprise! -- Object.create() does not take an object as the second argument.

Object.create() only has a single argument above, it creates an empty object.

Yes, sorry. However if Object.create took a simple object in the second argument then you don't need to repurpose extend() to create an object from a prototype and some properties, Object.create() can do it.

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

Since we live in a right to left world (a = b();) we need the target on the left. Then multiple RHS also works easily.

Swapping the parameters would is fine with me, then the name should probably be changed to something like copyOwnFrom(target, source).

However, note that if Object.create() were fixed: var clone = Object.create(Object.getPrototypeOf(orig), Object.getOwnProperties(orig));

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create().

Just allow the second argument to be property descriptor or object.

But, in a way, the proto operator <| is that fix.

Well as far as I can make out from harmony:proto_operator that operator is limited to member expressions.

jjb

# Axel Rauschmayer (14 years ago)

Sorry to be dense, but I still don't get it. How can an object have duplicate properties? I understand that own properties override properties on prototypes.

When you look at the object literal var source = { foo: 123 }; then you have e.g. "toString" in source === true "hasOwnProperty" in source === true When doing var target = {}; Object.extend(target, source) then target gets all those properties again (in addition to inheriting them from Object.prototype). target.hasOwnProperty(toString) === true Object.getPrototypeOf(target).hasOwnProperty === true

The following illustrates duplicate properties: var proto = { foo: 123 }; var obj = Object.create(proto); console.log(obj.foo === 123); // true obj.foo = 345; console.log(proto.foo === 123); // true

Thus, the property "foo" exists twice.

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create().

Just allow the second argument to be property descriptor or object.

Problem: property descriptors are indistinguishable from normal objects, they are normal objects.

But, in a way, the proto operator <| is that fix.

Well as far as I can make out from harmony:proto_operator that operator is limited to member expressions.

Right, my bad, the rhs has to be an object literal. But that still takes are of many (if not most) use cases.

# John J Barton (14 years ago)

On Fri, Oct 7, 2011 at 9:16 AM, Quildreen Motta <quildreen at gmail.com> wrote:

2011/10/7 John J Barton <johnjbarton at johnjbarton.com>

On Fri, Oct 7, 2011 at 8:34 AM, Axel Rauschmayer <axel at rauschma.de>wrote:

If you do something like var fuz = Object.extend(foo, {paper:'in', shoes:'my'});

Then fuz will get all properties of Object.prototype, again, as duplicates. In the above, you are clearly most interested in what you see in the literal and those are the own properties.

I don't understand how you can get properties as duplicates in JS. I disagree, since in my example I specifically point out that fuz.bar() should work.

You can't get duplicate properties, because keys are unique. However, you'll still get loads of clutter in the target object. Also, again, I'm not sure the majority of the use-cases would be concerned with copying the properties in the whole prototype chain -- that's expensive! -- and you could achieve the latter easily by recursively extending the object.

Again I'll ask you to consider that fuz.bar() should work. That is what extend means. How can we solve this?

We don't need to flatten the prototype chain, we can extend it. Object.extend(a,b) can return an object whose own properties are the own properties of |b| on those of |a| and the prototype chain of |b| appended to the prototype chain of |a|, dropping duplicate prototypes.

The overhead of these kinds of operations is insignificant, we are constructing objects not using them. Flattening the prototype chain would trade more memory for faster property access. Either choice will be wrong from some cases, but this is a second priority issue.

And this kind of "extend" enables poor man’s cloning as follows: var orig = { foo: "abc" }; var clone = Object.extend(Object.create(Object.getPrototypeOf(orig)), orig);

Sadly this code will fail since -- surprise! -- Object.create() does not take an object as the second argument.

Object.create here is only taking a single parameter though. `orig' is being passed to Object.extend. The kind of problems that arise when you have overtly verbose qualified names that you can't even keep track of what's where.

Perhaps, but this case I just wanted Axel to be wrong in the same way I had been wrong, so I didn't read his code carefully.

jjb

# Axel Rauschmayer (14 years ago)

Again I'll ask you to consider that fuz.bar() should work. That is what extend means. How can we solve this?

It’s possible that you are actually looking for mixins: javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins

One possible solution is to do 2 extends: one for the objects and one for the prototypes. That way, methods remain at the prototype level (as they normally should).

# Quildreen Motta (14 years ago)

2011/10/7 John J Barton <johnjbarton at johnjbarton.com>

On Fri, Oct 7, 2011 at 9:16 AM, Quildreen Motta <quildreen at gmail.com>wrote:

2011/10/7 John J Barton <johnjbarton at johnjbarton.com>

On Fri, Oct 7, 2011 at 8:34 AM, Axel Rauschmayer <axel at rauschma.de>wrote:

If you do something like var fuz = Object.extend(foo, {paper:'in', shoes:'my'});

Then fuz will get all properties of Object.prototype, again, as duplicates. In the above, you are clearly most interested in what you see in the literal and those are the own properties.

I don't understand how you can get properties as duplicates in JS. I disagree, since in my example I specifically point out that fuz.bar() should work.

You can't get duplicate properties, because keys are unique. However, you'll still get loads of clutter in the target object. Also, again, I'm not sure the majority of the use-cases would be concerned with copying the properties in the whole prototype chain -- that's expensive! -- and you could achieve the latter easily by recursively extending the object.

Again I'll ask you to consider that fuz.bar() should work. That is what extend means. How can we solve this?

We don't need to flatten the prototype chain, we can extend it. Object.extend(a,b) can return an object whose own properties are the own properties of |b| on those of |a| and the prototype chain of |b| appended to the prototype chain of |a|, dropping duplicate prototypes.

I believe that would be solved more effectively with traits (Self traits, I haven't had the time to read JS's proposal on that yet).

# John J Barton (14 years ago)

On Fri, Oct 7, 2011 at 10:04 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

Sorry to be dense, but I still don't get it. How can an object have duplicate properties? I understand that own properties override properties on prototypes.

When you look at the object literal var source = { foo: 123 }; then you have e.g. "toString" in source === true "hasOwnProperty" in source === true When doing var target = {}; Object.extend(target, source) then target gets all those properties again (in addition to inheriting them from Object.prototype). target.hasOwnProperty(toString) === true Object.getPrototypeOf(target).hasOwnProperty === true

The following illustrates duplicate properties: var proto = { foo: 123 }; var obj = Object.create(proto); console.log(obj.foo === 123); // true obj.foo = 345; console.log(proto.foo === 123); // true

Thus, the property "foo" exists twice.

The object |obj| has a property |foo|; the object |proto| has a property |foo|. The string "foo" appears as property names in two different objects. I don't understand what problem this will cause for developers. The object |obj| does not have duplicate properties, it has a unique value for each property name.

I don’t see a simple way of “fixing” (property descriptors do have their

uses) Object.create().

Just allow the second argument to be property descriptor or object.

Problem: property descriptors are indistinguishable from normal objects, they are normal objects.

If you pass a normal object to object.Create() you get an error message. Clearly they can be distinguished.

jjb

# Axel Rauschmayer (14 years ago)

The object |obj| has a property |foo|; the object |proto| has a property |foo|. The string "foo" appears as property names in two different objects. I don't understand what problem this will cause for developers. The object |obj| does not have duplicate properties, it has a unique value for each property name.

Depending on what you want, it’s not a problem.

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create().

Just allow the second argument to be property descriptor or object.

Problem: property descriptors are indistinguishable from normal objects, they are normal objects.

If you pass a normal object to object.Create() you get an error message. Clearly they can be distinguished.

var obj = Object.create(proto, { p: { value: 42 } })

How would JS distinguish between the property descriptor { value: 42 } and an object whose property "value" has the value 42?

# Allen Wirfs-Brock (14 years ago)

(I'm starting a little late on a long thread so sorry if I repeat things that have already been said.

On Oct 7, 2011, at 1:50 AM, John-David Dalton wrote:

On Fri, Oct 7, 2011 at 10:17 AM, Jake Verbaten <raynos2 at gmail.com> wrote:

Object.extend is the only method I shim on every es5 project, so +1

However arguments like library X is would break are stupid, remember es6 is an opt-in so backwards compat with existing code be damned.

I've suggested that Object.merge(targetObj, sourceObj) might be a reasonable name that avoids conflict with the most common existing usages.

No U! My concern came from seeing ES5 additions like Function#bind partially hindered because spec'ed behavior isn't covered by most shims/existing lib implementations. For example using the bound function's behavior with the new operator is inconsistent with most third party code/shims. But since it's opt-in yes you can muck with dev expectations and change the behavior of additions that have existed for 6yrs or so :D

ES6 is not opt-in at the library level. Property additions to the built-ins are visible to code that has not opt-ed in to ES6 mode.

The only exception would be new built-in objects that were made available via explicit import of built-in modules. So far, we don't have any but it is a possibility for new globally named built-ins.

As for the functionality, it should just be an n-ary function which extends the first object with all the own properties of the other n-1 objects. If keys clash give right precedence.

I've used this flavor of extends too (minus the own properties part).

Whether you do or don't want only own properties (or for that matter only enumerable properties or only non-private named properties) depends entirely upon your use case and object abstraction style. There is no universally right or wrong answer choice for any of these decisions.

whether key clashes favour left or right isnt important. whether the return value is undefined or the first object isnt important.

It may not be important to you but a specification must deal with such issues and decisions have both usage and implementation consequences. That is one reason that specifications sometimes don't include what might seem like obvious generalizations. If there isn't an obvious right answer to such issues it is often better to leave it to library writers to make such choices.

Yap but chaining FTW.

please dont try arguments from authority like bad library X copies over enumerable properties from the prototype chain so we should too.

If that part is opt-in then sure go for implementing it better than existing libs but why not push for the monocle operator?

BTW, I consider .{ (I've actually starting to favor calling it "stache") a quite different operator from Object.extend that addresses different use cases. We need them both.

# John J Barton (14 years ago)

On Fri, Oct 7, 2011 at 11:08 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

I don’t see a simple way of “fixing” (property descriptors do have their

uses) Object.create().

Just allow the second argument to be property descriptor or object.

Problem: property descriptors are indistinguishable from normal objects, they are normal objects.

If you pass a normal object to object.Create() you get an error message. Clearly they can be distinguished.

var obj = Object.create(proto, { p: { value: 42 } })

How would JS distinguish between the property descriptor { value: 42 } and an object whose property "value" has the value 42?

I would interpret this as a descriptor; if the dev wanted a property they would say {p:42};

But let's give up on this line, it will never be accepted here. Let's concentrate on how to create objects given that Object.create(proto) gives an empty object with a prototype of |proto|. Could Object.extend() be defined so devs can combine it with Object.create()?

jjb

# Allen Wirfs-Brock (14 years ago)

On Oct 7, 2011, at 9:13 AM, Juan Ignacio Dopazo wrote:

On Fri, Oct 7, 2011 at 12:34 PM, Axel Rauschmayer <axel at rauschma.de> wrote: I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

Just to provide some background. Though process that went into choosing the names in ES5 was that functions that were viewed as meta level operations that would primarily be used by fairly sophisticated library designers were given long multiword names, eg Object.getOwnPropertyNames. Functions that were viewed as application level operations that would be widely used by app developers where given short names, eg Object.keys and Object.create.

The assumption was that the meta level operations would be used by library writers to create new app level operations that normally would have short everyday names. Also, the long names minimized the chance that the names would conflict with existing libraries or applications.

Axel's name seems consistent with that model. However, my perceptions is that they will be unacceptable to many vocal members of our community.

# Allen Wirfs-Brock (14 years ago)

On Oct 7, 2011, at 9:23 AM, Axel Rauschmayer wrote:

...

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

I isn't clear from the attribution who said the following. It wasn't Axel:

Since we live in a right to left world (a = b();) we need the target on the left. Then multiple RHS also works easily.

There is probably a significant part of the world population who would disagree with this assertion. We have building technology for web developer across the entire world. We need to be careful about using cultural bias to justify language design decisions.

You might have a stronger argument is you said that JS already has a right to left bias and that consistency is important.

Swapping the parameters would is fine with me, then the name should probably be changed to something like copyOwnFrom(target, source).

However, note that if Object.create() were fixed: var clone = Object.create(Object.getPrototypeOf(orig), Object.getOwnProperties(orig));

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create(). But, in a way, the proto operator <| is that fix. or, an extended new operator:

   clone = new orig

or .{

depending upon the exact use case. However, the key thing is that things like <|, .{, and possible extensions to new are intended to be directly used in application code for very common use cases Things like Object.create(Object.getPrototypeOf(orig), Object.getOwnPropertyDescriptors(orig)) are best encapsulated within library functions that are simply named to address some specific usecase.

However, your are on to something with the above which will work as follows in ES.next: var clone = Object.create(Object.getPrototypeOf(orig), Object.getOwnPropertyDescriptors(orig));

Except that things like [[Super]] bindings and private named keys significant complicate the matter.

# John J Barton (14 years ago)

On Fri, Oct 7, 2011 at 12:13 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Oct 7, 2011, at 9:23 AM, Axel Rauschmayer wrote:

...

I would prefer the name Object.copyOwnPropertiesTo(source, target) or Object.copyOwnTo(source, target) to the name “extend” (which, to me, suggests inheritance).

I isn't clear from the attribution who said the following. It wasn't Axel:

Since we live in a right to left world (a = b();) we need the target on the left. Then multiple RHS also works easily.

There is probably a significant part of the world population who would disagree with this assertion. We have building technology for web developer across the entire world. We need to be careful about using cultural bias to justify language design decisions.

You might have a stronger argument is you said that JS already has a right to left bias and that consistency is important.

Yes, that is what I said: JS is a right to left world. I shall try to avoid being obscure.

jjb

# Brendan Eich (14 years ago)

On Oct 7, 2011, at 11:39 AM, John J Barton wrote:

On Fri, Oct 7, 2011 at 11:08 AM, Axel Rauschmayer <axel at rauschma.de> wrote:

I don’t see a simple way of “fixing” (property descriptors do have their uses) Object.create().

Just allow the second argument to be property descriptor or object.

Problem: property descriptors are indistinguishable from normal objects, they are normal objects.

If you pass a normal object to object.Create() you get an error message. Clearly they can be distinguished.

var obj = Object.create(proto, { p: { value: 42 } })

How would JS distinguish between the property descriptor { value: 42 } and an object whose property "value" has the value 42?

I would interpret this as a descriptor; if the dev wanted a property they would say {p:42};

But let's give up on this line, it will never be accepted here.

Or anywhere. The situation is hopelessly ambiguous, or, if you insist on classifying all the variations on property descriptor values in an object passed as Object.create's second argument as "not normal objects", future-hostile. We may want to extend property descriptors in the future. The traitsjs.org library already does so.

Let's concentrate on how to create objects given that Object.create(proto) gives an empty object with a prototype of |proto|. Could Object.extend() be defined so devs can combine it with Object.create()?

Sure, Object.create(proto).extend({k1:v1, ... kN:vN}).

If you want to make that one method instead of two, a new Object.createSimple or better name would be needed.

# Axel Rauschmayer (14 years ago)

Sure, Object.create(proto).extend({k1:v1, ... kN:vN}).

If you want to make that one method instead of two, a new Object.createSimple or better name would be needed.

Such a method would be very useful independently of this use case!

I would also love to have turn Object.defineProperties into a similar method. Naming-wise, one could introduce a new system, for example:

  • Object.createSimple(proto, props?) versus Object.createDesc(proto, descs?)
  • Object.prototype.extendSimple(props) versus Object.prototype.extendDesc(descs) => use something more complex than "extend" to avoid name clashes.

Advantage: Mix normal property definitions with property descriptors:

var obj = {
    count: 3,
}. extendDesc({ MAX_COUNT: { value: 10, writable: false, configurable: false } });

== Big picture ==

  1. The two most prevalent use cases are handled by:

    • property definitions in object literals (thanks to Allen’s operators, those can be used in many more places now).
    • the proposed "#" prefix for read-only properties. => easy to understand new syntax, lightweight (little grawlixing), sticking to 80-20.
  2. More complex requirements can be handled by using property descriptors.

.#2 would become more lightweight by additional syntax, but it seems like that has already been discussed and rejected: var obj = { count: 3, MAX_COUNT := { value: 10, writable: false, configurable: false }, }

Thus: If one uses ":=" (previously: const-ification) then the RHS is a property descriptor. This would also be nicely future-proof with regard to possible upcoming property descriptor extensions.