Batch assignment functions

# Rick Waldron (7 years ago)

Recently, Allen produced a strawman proposal[0][1] for the "object define properties" operator, which was designed to provide syntax that differentiated semantics of define vs. assign. Towards the end of the thread, IIRC, Brendan suggested some new Object functions: Object.define() and Object.assign().[2]

I spent time over the weekend preparing common use cases for batch assignment based on real world examples from jQuery[3], Dojo[4], Node.js[5] and Lodash (an improved Underscore)[6][7] (With all due respect, Prototype is no longer considered a relevant library for use in modern day web development). Initially, I assumed that the jQuery "deep extend" was the common case and drafted an attempt at handling "nested assignment" or "deep extending". Off-list, Dave Herman reminded me that there is no way to know what the user actually wants with regard to nested object properties:

  1. Target property does not exist: define source value
  2. Target property exists, its value is anything except a nested object: Assign source value
  3. Target property exists, its value is a nested object, the source property value is anything except a nested object: Assign source value

The ambiguity:

  1. Target property exists, its value is a nested object, the source property is a nested object: _____?

In the trenches we use an icky boolean trap[8] flag to tell the jQuery API what we want; jQuery.extend( true, a, b ) will assume you want to "deep merge/extend" b into a and will do so by using a pure evil function called jQuery.isPlainObject to determine if an object was created from Object or {}. Regardless, this is not an arguable use case, it's a reality.

As a result, I re-drafted Object.assign based on the real-world use cases, but specifically does not attempt nested object property assignment recursion. At this point I still believe that the deep nested assignement case is strong enough to consider, but I'm not sure how to approach it. It might warrant its own implicit "merge object properties whenever possible"... Object.merge?

In summary, based on findings so far, I'd like to propose the following:

In all cases, "target" refers to an object in the "dictionary of values" or "bag of properties" sense. "source" can be any kind of object that has own properties.

Object.define( target, source ): defineProperties w/ sensible defaults (w, e, c: true).

Object.put( target, source ): is... "put"! (gist.github.com/3350283)

Object.merge( target, source ): "deep put", ideally this would drill down into nested objects that "line up" on the left and right, [[Put]]ing values from the right onto the left. I want to make it perfecty clear that this is desired, but the semantics are not yet clear.

-Rick

[0] esdiscuss/2012-August/024402

[1] strawman:define_properties_operator

[2] esdiscuss/2012-August/024477

[3] jquery/jquery/blob/master/test/unit/core.js#L866-973

[4] dojotoolkit.org/reference-guide/1.7/dojo/mixin.html#dojo-mixin

[5] joyent/node/blob/master/test/simple/test-util.js#L73-80(note: I included this, but primarily, users will require underscore or lodash for such utilities)

[6] bestiejs/lodash/blob/master/test/test.js#L375-399

[7] documentcloud/underscore/blob/master/test/objects.js#L30-41

[8] ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html

# Brendan Eich (7 years ago)

Rick Waldron wrote:

As a result, I re-drafted Object.assign based on the real-world use cases, but specifically does not attempt nested object property assignment recursion. At this point I still believe that the deep nested assignement case is strong enough to consider, but I'm not sure how to approach it. It might warrant its own implicit "merge object properties whenever possible"... Object.merge?

Object.merge, maybe -- but definitely do not want this muddled into assign/define or put/define or whatever those shallow-only names should be.

In summary, based on findings so far, I'd like to propose the following:

In all cases, "target" refers to an object in the "dictionary of values" or "bag of properties" sense. "source" can be any kind of object that has own properties.

Object.define( target, source ): defineProperties w/ sensible defaults (w, e, c: true).

Object.put( target, source ): is... "put"! (gist.github.com/3350283)

Some questions:

  1. Why implicitly bind any function value?

  2. Ditto for get and set accessor functions?

  3. Why Object.defineProperty if !(key in target)? Why not just always assign, since this is Object.put and not Object.define?

# Rick Waldron (7 years ago)

On Wed, Aug 15, 2012 at 5:30 PM, Brendan Eich <brendan at mozilla.org> wrote:

Rick Waldron wrote:

As a result, I re-drafted Object.assign based on the real-world use cases, but specifically does not attempt nested object property assignment recursion. At this point I still believe that the deep nested assignement case is strong enough to consider, but I'm not sure how to approach it. It might warrant its own implicit "merge object properties whenever possible"... Object.merge?

Object.merge, maybe -- but definitely do not want this muddled into assign/define or put/define or whatever those shallow-only names should be.

Agreed, which was the motivation for the three distinct APIs below

In summary, based on findings so far, I'd like to propose the following:

In all cases, "target" refers to an object in the "dictionary of values" or "bag of properties" sense. "source" can be any kind of object that has own properties.

Object.define( target, source ): defineProperties w/ sensible defaults (w, e, c: true).

Object.put( target, source ): is... "put"! (gist.github.com/** 3350283 gist.github.com/3350283)

Some questions:

  1. Why implicitly bind any function value?

The code in the gist should be regarded as a loose approximation of what I think we should try to achieve — that said, it was actually just a modified version of my API-ified implementation of Allen's define properties operator :)

  1. Ditto for get and set accessor functions?

Same answer

  1. Why Object.defineProperty if !(key in target)? Why not just always assign, since this is Object.put and not Object.define?

This part I was actually unsure of and it shows here; I made an assumption that doesn't hold now that you've pointed it out and I've had to re-think my motivation. So really, this can and should just be [[Put]]

I'll update the gist.

# David Bruant (7 years ago)

Le 15/08/2012 21:42, Rick Waldron a écrit :

Recently, Allen produced a strawman proposal[0][1] for the "object define properties" operator, which was designed to provide syntax that differentiated semantics of define vs. assign. Towards the end of the thread, IIRC, Brendan suggested some new Object functions: Object.define() and Object.assign().[2]

I spent time over the weekend preparing common use cases for batch assignment based on real world examples from jQuery[3], Dojo[4], Node.js[5] and Lodash (an improved Underscore)[6][7] (With all due respect, Prototype is no longer considered a relevant library for use in modern day web development). Initially, I assumed that the jQuery "deep extend" was the common case and drafted an attempt at handling "nested assignment" or "deep extending". Off-list, Dave Herman reminded me that there is no way to know what the user actually wants with regard to nested object properties:

  1. Target property does not exist: define source value
  2. Target property exists, its value is anything except a nested object: Assign source value
  3. Target property exists, its value is a nested object, the source property value is anything except a nested object: Assign source value

The ambiguity:

  1. Target property exists, its value is a nested object, the source property is a nested object: _____?

This seems to be close to the issue of copying an object. What is exactly expected is as much ambiguous.

I would add to the ambiguity prototypal inheritance. All properties of WebIDL objects are inherited getters. If you want to extend an object with the values of a WebIDL object, do you loop through inherited properties? (actually, if you don't, you have no property at all) To the ambiguity, we can also add whether non-enumerable properties should be assigned or not. Facing function, do we want another function (callable with same body) or juste the properties. And finally, facing accessors, do we want to call the getter or copy it? Bound to the original object it was extracted from or working with the new object?

I'm of course not asking for answer, just enumerating other sources of ambiguity