ES3.1 Object static methods rationale document
On Jul 15, 2008, at 10:30 PM, Allen Wirfs-Brock wrote:
I’ve up loaded to the wiki a new document titled: “Proposed
ECMAScript 3.1 Static Object Functions: Use Cases and Rationale”It’s available as both a pdf and as a Word doc file:
lib/exe/fetch.php?id=es3.1% 3Aes3.1_proposal_working_draft&cache=cache&media=es3.1:rationale_for_e s3_1_static_object_methods.pdf
lib/exe/fetch.php?id=es3.1% 3Aes3.1_proposal_working_draft&cache=cache&media=es3.1:rationale_for_e s3_1_static_object_methods.doc
Hi Allen,
Good to see rationales. A few comments:
- No rationale responding to the thread containing this message:
mail.mozilla.org/pipermail/es4-discuss/2007-September 001114.html
that questions the wisdom of getPrototypeOf. The other rationales are
helpful, the lack of one responding to this public thread questioning
essentially the same design element is a lack -- what do you think?
-
getProperty and getProperties seem misnamed in light of common
usage of "get", "[[Get]]", "getProperty", etc. all connoting value- getting, not descriptor-getting. getPropertyDescriptor is a bit long,
but not fatally so. Worth renaming? -
Did you consider prototype's Object.extend method:
Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };
(see www.prototypejs.org/assets/2007/11/6/prototype.js)? It's
a commonly encountered "shallow enumerable property clone". John
Resig enquired about it being in ES3.1 drafts, but it's not there.
Any particular reason why not?
On Jul 15, 2008, at 11:50 PM, Brendan Eich wrote:
- getProperty and getProperties seem misnamed in light of common
usage of "get", "[[Get]]", "getProperty", etc. all connoting value- getting, not descriptor-getting. getPropertyDescriptor is a bit
long, but not fatally so. Worth renaming?
Shorter alternative verbs to "get": lookup, query. The analogy is
lookup : define :: get : put.
On Jul 16, 2008, at 12:09 AM, Brendan Eich wrote:
On Jul 15, 2008, at 11:50 PM, Brendan Eich wrote:
- getProperty and getProperties seem misnamed in light of common usage of "get", "[[Get]]", "getProperty", etc. all connoting value- getting, not descriptor-getting. getPropertyDescriptor is a bit long, but not fatally so. Worth renaming?
Shorter alternative verbs to "get": lookup, query. The analogy is lookup : define :: get : put.
That was unclear, sorry. I meant to suggest that "lookupProperty" is
a shorter alternative to "getPropertyDescriptor". Using "lookup" or
"query" relieves the need for "Descriptor" at the end to disambiguate
value- from descriptor-getting. So:
// returns descriptor if (name in obj), else null or something falsy [1] Object.lookupProperty(obj, name)
It's still longer than Object.getProperty, but Object.getProperty
seems like a misnomer every time I read it, since it does not do a
[[Get]] or [[GetProperty]]. ECMA-262 does not need more overloadings
of "get-property" names.
Similar comments apply to Object.getOwnProperty.
/be
[1] The 15 July 2008 draft specifies false, not null, as the return
value of Object.getProperty(O, P) when !(P in O) -- is this intended?
Brendan Eich wrote:
- No rationale responding to the thread containing this message:
mail.mozilla.org/pipermail/es4-discuss/2007-September/001114.html
that questions the wisdom of getPrototypeOf. The other rationales are helpful, the lack of one responding to this public thread questioning essentially the same design element is a lack -- what do you think?
The motivation for these methods is to allow Ajax libraries and the Caja runtime to harden the environment. Such a library can use and them remove these methods, disallowing access by guest code.
- Did you consider prototype's Object.extend method:
Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };
Yes we did.
I didn't specifically respond to that thread because I wasn't aware of it. I had intended to mention proto as a precedent but it slipped through the cracks.
It's true that proto or getPrototypeOf breaks an object's encapsulation barrier and reveals implementation details that perhaps were intended to be hidden. The same could be said about the proposed getProperty function which, among other things, gives an observer access to the functions that implement a getter/setter property. In general, that's the nature of reflection. Overall, I think that this is a situation that is inherent in our current generation of dynamic languages. They tend to depend upon the use of idioms that require penetration of the encapsulation barrier.
Some of the concerns expressed in that thread are address by other aspects of the static Object methods proposal. For example, the integrity of prototype objects can be protected by sealing them in whole or in part to prevent tampering. Also note, that while we support inspection of the prototype value, we don't support modification of it.
As Doug implies below, one reason for making these operations "static methods" was to make it easier to control access to them. It you are creating some sort of sandbox, you may not want to make them available within it. That could be taken as a argument in favor of hanging them off of a dedicated global Meta object rather than off of Object. It may be slightly easier to wholesale restrict access to Meta than it would be to restrict access to just these methods while still providing access to the Object constructor.
Another already available technique for obtaining the same information in many situations that wasn't mentioned in the thread is to use Object.prototype.isPropertyOf as a probe to discover the prototypes of objects. It isn't something that you would want to do in production code but I don't think that anyone who was trying to crack an application would hesitate from doing.
Arguably, some of the need for direct prototype access is alleviated by providing the clone method. However, there are still plenty of other situations where it is useful.
I don't agree with Lars' contention that proto and function.caller present the same kind of problem. True, they both break abstraction barriers, but different barriers. proto breaks the object implementation abstraction layer. Function.caller breaks the call stack abstraction. Crossing the object implementation boundary is generally required for defining object abstractions in this language and proto only reveals information that in most cases is already available through other, less direct, means. In contrast, function.caller reveals information about the call stack that is not normally reified in this language or generally needed (those who consider continuations the ultimate programming abstraction may disagree).
In summary, not providing reflective access to an object's prototype doesn't really provide any real security, it just makes some useful tasks less convenient. Reverting to barnyard analogies: the barn door is already wide open and we're debating an inch wide "trench" that spans the opening. If we want to keep the horses in we need to think about how to put an iron gate across that gap rather than worrying about the risks of filling in the trench.
On Jul 16, 2008, at 5:39 AM, Douglas Crockford wrote:
- Did you consider prototype's Object.extend method:
Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };
Yes we did.
And? The doc gives rationales for design decisions. What's the
rationale for leaving Object.extend out?
On Wed, Jul 16, 2008 at 10:11 AM, Brendan Eich <brendan at mozilla.org> wrote:
And? The doc gives rationales for design decisions. What's the rationale for leaving Object.extend out?
If the document needs to give rationales for leaving out each thing we did not include, it would be quite a long document. What is the argument for adding Object.extend()? A pointer to Resig's message or a prior discussion is an adequate response.
On Jul 16, 2008, at 8:28 AM, Allen Wirfs-Brock wrote:
I didn't specifically respond to that thread because I wasn't aware
of it. I had intended to mention proto as a precedent but it
slipped through the cracks.
No problem. I wanted to point it out so that the rationale doc might
include it.
It's true that proto or getPrototypeOf breaks an object's
encapsulation barrier and reveals implementation details that
perhaps were intended to be hidden. The same could be said about
the proposed getProperty function which, among other things, gives
an observer access to the functions that implement a getter/setter
property. In general, that's the nature of reflection. Overall, I
think that this is a situation that is inherent in our current
generation of dynamic languages. They tend to depend upon the use
of idioms that require penetration of the encapsulation barrier.
Yeah, I mentioned that in the thread. It's more fundamental than a
temporary lack of the current generation of dynamic langauges.
Reflection breaks abstraction, removing some free theorems -- news at
11 ;-).
Some of the concerns expressed in that thread are address by other
aspects of the static Object methods proposal. For example, the
integrity of prototype objects can be protected by sealing them in
whole or in part to prevent tampering.
This is a good point. SpiderMonkey and Rhino have had Seal APIs for
years for this reason (shared prototypes across thread and trust
boundaries must be immutable).
One feature of both Seal APIs is the ability to seal an entire object
graph. This is a sharp tool, since most graphs are fully connected
and if you seal the world, life gets boring fast. But it is handy for
setting up sealed standard object constructors/prototypes/methods
trees with one API call, at the beginning of the world in a throwaway
global object that (because of [[Scope]] links) gets sealed too due
to the transitive closure.
Also note, that while we support inspection of the prototype value,
we don't support modification of it.
I noticed ;-).
As Doug implies below, one reason for making these operations
"static methods" was to make it easier to control access to them.
It you are creating some sort of sandbox, you may not want to make
them available within it.
Yes, the static Object method suite is a good idea for that reason,
as well as for not intruding on the prototype-delegated property
space of all objects.
That could be taken as a argument in favor of hanging them off of
a dedicated global Meta object rather than off of Object. It may
be slightly easier to wholesale restrict access to Meta than it
would be to restrict access to just these methods while still
providing access to the Object constructor.
Let's not bring back Meta to ES3.1, it is not wanted in ES4.
We should reconcile strict modes too, but that's a different topic --
except insofar as 3.1's strict mode locks down Object.defineProperty,
Object.getPrototypeOf, etc. So the host code that removes
Object.getPrototypeOf from a guest's sandbox can't be running in
strict mode. I'm not suggesting this is a problem, just noting it.
Another already available technique for obtaining the same
information in many situations that wasn't mentioned in the thread
is to use Object.prototype.isPropertyOf as a probe to discover the
prototypes of objects. It isn't something that you would want to do
in production code but I don't think that anyone who was trying to
crack an application would hesitate from doing.
Searching the reachable object graph would not only be time-
consuming, it could fail to find prototypes that were hidden in
disconnected components. The case of an otherwise-unreachable
prototype object was discussed in that thread.
Arguably, some of the need for direct prototype access is
alleviated by providing the clone method. However, there are still
plenty of other situations where it is useful.
I observe that proto in SpiderMonkey- and Rhino-based JS is
mostly used for cases covered by Object.create, with a minority use-
case that we've discussed before initializing it to null in object
initialisers to make maps (dictionaries).
I'm convinced based on this experience that proto is the tempting
but wrong low-level "API" to handle these use-cases. I'm in favor of
the higher level APIs such as create, clone, and ES4 Map or similar,
provided they have the sweet syntax needed to keep kids off the
attractive nuisances with which they compete.
Crossing the object implementation boundary is generally required
for defining object abstractions in this language
Not generally. Constructor functions and .prototype properties go a
long way, and no one has been able to use proto in portable JS on
the web, yet life goes on -- and people do define a great many object
abstractions in JS libraries.
In summary, not providing reflective access to an object's
prototype doesn't really provide any real security, it just makes
some useful tasks less convenient.
I agree with you here in general, and it's good to hear that
reflection is not the enemy of "real security".
On Jul 16, 2008, at 10:26 AM, Mark S. Miller wrote:
On Wed, Jul 16, 2008 at 10:11 AM, Brendan Eich
<brendan at mozilla.org> wrote: And? The doc gives rationales for design decisions. What's the rationale for leaving Object.extend out?If the document needs to give rationales for leaving out each thing
we did not include, it would be quite a long document.
It's pretty long already, yet it dotes on some issues that are less
relevant than Object.extend, as demonstrated by all the Ajax code
that uses Object.extend but does without, e.g., Object.getPrototypeOf
(or proto). Do what you want with the doc, but please don't
dismiss particular requests for rationales with general fretting
about document length.
The issue of draft ES3.1 adding a great many Object APIs, yet not
adding one of the most common APIs from popular Ajax libraries, is
legitimate to raise. The answer to my question may not lead to a
rationale being added to the document, but there ought to be an
answer other than "no" -- or onlookers will rightly suspect that
there something is wrong in the reasoning behind the rationales.
What is the argument for adding Object.extend()? A pointer to
Resig's message or a prior discussion is an adequate response.
bugzilla.mozilla.org/show_bug.cgi?id=433351
The argument for Object.extend is similar to the one for
Function.bind. Different use-cases, but common re-implementation in
real-world code. Both can be built using ES3, but relieving everyone
from having to re-invent and re-download these wheels is one of the
main purposes of the standard library.
Brendan Eich wrote:
That was unclear, sorry. I meant to suggest that "lookupProperty" is
a shorter alternative to "getPropertyDescriptor". Using "lookup" or
"query" relieves the need for "Descriptor" at the end to disambiguate
value- from descriptor-getting. So:
I find inspectProperty() a little more descriptive and intuitive.
I actually don't expect "mere mortal" ECMAScript programmers to be confused with [[get]] as I won't expect them to have ever been exposed to it. Scanning the rest of the standard ECMAScript objects, the only methods that use a "get" prefix seem to be in Date so this may be some potential for a confusion of conventions there but on the other hand Date seems to largely a world onto itself so I don't know if its usage should really be a concern. A bigger issue may be conventions that have been adopted by the more popular AJAX frameworks, if they are in conflict with the getProperty usage. I admit I'm not real familiar with the detailed APIs of the frameworks but I'm sure somebody here can enlighten me.
Another alternative to "get" would be "reify", eg reifyProperty. My immediate reaction is that it would not be very approachable for unsophisticated users (in other words, most of them). On the other hand, I could argue that if you don't understand what reify means in this context you shouldn't be using the function. And, this is one of the rare case where the dictionary definition fairly precisely corresponds to the technical meaning. This one actually kind of grows on me, but I don't think I could really champion it unless there as a spontaneous outburst of support for it.
I could live with lookup, although I think it focuses the meaning on the access process rather than on the result. Another, slightly longer alternative would be "retrieve".
Regarding, what getOwnProperty returns, what you currently see in the spec. is probably a bug. My intent was for it to return undefined, although somebody more steeped in JavaScript idioms could convince me that null is more appropriate if that really is the case. The internal function FromPropertyDescriptor probably also needs to return that same value under the appropriate circumstances. Finally, here's another bug: step 3 of Object.getProperty should call [[GetProperty]] rather than [[GetOwnProperty]]. Also step 4 has a "]]" that should be there.
On Jul 16, 2008, at 11:35 AM, Allen Wirfs-Brock wrote:
I could live with lookup, although I think it focuses the meaning
on the access process rather than on the result. Another, slightly
longer alternative would be "retrieve".
What do you say to Ingvar's suggestion of "inspect"?
Regarding, what getOwnProperty returns, what you currently see in
the spec. is probably a bug.
Are you tracking these somewhere? I think bugs.ecmascript.org is a
fine way to keep trac(k). :-)
My intent was for it to return undefined, although somebody more
steeped in JavaScript idioms could convince me that null is more
appropriate if that really is the case.
JS has two bottoms: null means no object and undefined means no
value, so for this kind of "descriptor object if property exists,
else bottom" API, null is better.
On Jul 16, 2008, at 11:44 AM, Brendan Eich wrote:
On Jul 16, 2008, at 11:35 AM, Allen Wirfs-Brock wrote:
I could live with lookup, although I think it focuses the meaning on the access process rather than on the result. Another, slightly longer alternative would be "retrieve".
What do you say to Ingvar's suggestion of "inspect"?
Or (drum roll) "describe": describeProperty, which returns a property
descriptor.
(I'm not going to get you to take the bait on "reify", am I?)
I think I like "describe" better than "inspect" for no particularly tangible reason, although it does have more characters. I generally find the Thesaurus a useful tool in this process and it turned up "depict" which is shorter but also seems to capture the same core distinction as "describe".
I think that the currently named getOwnProperty is more fundamental than getProperty so in considering length we should probably use the former as our benchmark. BTW, I'm open to arguments that we don't really need getProperty (as long as getPrototypeOf is kept). (Oh shit ... do we need to rename that one, too??)
I think we've pretty much covered the "name space" and would be content, at this point, to sit back for a few days and see if anybody else is brave enough to argue for one name over another. If not I think we can reach agreement on one of these that we have been discussing.
On Jul 16, 2008, at 12:31 PM, Allen Wirfs-Brock wrote:
(I'm not going to get you to take the bait on "reify", am I?)
(no way! ;-)
I think I like "describe" better than "inspect" for no particularly
tangible reason, although it does have more characters. I generally
find the Thesaurus a useful tool in this process and it turned up
"depict" which is shorter but also seems to capture the same core
distinction as "describe".
Length is less of an issue, given the rationale doc's points in favor
of "keyword parameters via object literals", etc.
I think that the currently named getOwnProperty is more fundamental
than getProperty so in considering length we should probably use
the former as our benchmark. BTW, I'm open to arguments that we
don't really need getProperty (as long as getPrototypeOf is kept).
(Oh shit ... do we need to rename that one, too??)
No, that's a value-get, not a descriptor-get. But you raise a good
point: defineProperty creates an own property. Is there really a need
for getProperty as drafted? If not, I'd favor making describeProperty
return null if the named property is not "own", but in a prototype.
What are use-cases for getProperty as distinct from getOwnProperty?
I think we've pretty much covered the "name space" and would be
content, at this point, to sit back for a few days and see if
anybody else is brave enough to argue for one name over another. If
not I think we can reach agreement on one of these that we have
been discussing.
Cool. I'm standing pat on describeProperty.
-----Original Message----- From: Brendan Eich [mailto:brendan at mozilla.org]
But you raise a good point: defineProperty creates an own property. Is there really a need for getProperty as drafted? If not, I'd favor making describeProperty return null if the named property is not "own", but in a prototype.
What are use-cases for getProperty as distinct from getOwnProperty?
For awhile, defineProperty was called defineOwnProperty but we eventually decided all properties are initially defined as own properties so the "Own" was redundant.
Right now, the best I can come up with is that it should be a relatively efficient way to test for the existence of a possibly inherited property. However, if that was really important it would probably be better to have a built-in function that specifically performed that test without creating a descriptor. We may have discussed some more compelling use cases that I don't now recall, in which case I'm sure someone will remind me.
In generally, I favor proving just the minimal "spanning set" of functions that can be used to build more comprehensive reflection models if desired. If there isn't a stronger use case for the version that considers inherited methods I'd be happy to eliminate it.
On Wed, Jul 16, 2008 at 1:16 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
-----Original Message----- From: Brendan Eich [mailto:brendan at mozilla.org]
Right now, the best I can come up with is that it should be a relatively efficient way to test for the existence of a possibly inherited property.
The "in" operator tests for the existence of a possibly inherited property.
var hasProp = (p in o);
Garrett
Brendan Eich wrote:
Cool. I'm standing pat on describeProperty.
/be
[I've removed es3.x-discuss, since I'm not a member]
The verb describe makes this sound like a setter method rather than a getter method. That is, describeProperty sounds like a short name for setPropertyDescriptor rather than getPropertyDescriptor.
Also, I think there is serious potential for confusion between defineProperty and describeProperty. "Define" and "describe" both begin with D and have similar meanings. Programmers are going to forget which method is which.
Brendan, I think you were correct when you originally wrote:
lookup : define :: get : put.
I think that lookupProperty is much nicer than describeProperty, since "lookup" captures the getter nature of the method in a way that "describe" does not.
Frankly, though, I imagine that defining properties will be more common than reflecting on them, and I don't see anything wrong with a long name that explicitly describes the function: getPropertyDescriptor(), lookupPropertyAttributes() or whatever.
David Flanagan
On Wed, Jul 16, 2008 at 1:41 PM, David Flanagan <david at davidflanagan.com> wrote:
Brendan Eich wrote:
Frankly, though, I imagine that defining properties will be more common than reflecting on them, and I don't see anything wrong with a long name that explicitly describes the function: getPropertyDescriptor(), lookupPropertyAttributes() or whatever.
I agree. 'getPropertyDescriptor' sounds more descriptive. Allen's doc calls the return an "attributeDescriptor" but "propertyDescriptor" seems to be a more descriptive term.
Garrett
Allen Wirfs-Brock wrote:
In summary, not providing reflective access to an object's prototype doesn't really provide any real security, it just makes some useful tasks less convenient. Reverting to barnyard analogies: the barn door is already wide open and we're debating an inch wide "trench" that spans the opening. If we want to keep the horses in we need to think about how to put an iron gate across that gap rather than worrying about the risks of filling in the trench.
On the other hand, providing reflective access to an object's prototype is harmful to compatibility because it prevents implementations from introducing intermediate prototypes without breaking the web. Consider the example of having just Numbers and later compatibly introducing more specific subkinds of Numbers.
Waldemar
Maybe someone could just give the rationale for leaving out Object.extend?
Douglas Crockford wrote that it was considered, but I'm confused since it looks like you haven't even seen a proposal, and didn't participate in the discussion to exclude it.
- Rob
2008/7/16 Mark S. Miller <erights at google.com>:
Working...
On Jul 16, 2008, at 1:41 PM, David Flanagan wrote:
Brendan, I think you were correct when you originally wrote:
lookup : define :: get : put.
I think that lookupProperty is much nicer than describeProperty, since "lookup" captures the getter nature of the method in a way that "describe" does not.
Connotations are many, ambiguity without a noun phrase (not just
overloaded old "property") saying what's being "got" or "described"
or "looked up" is inevitable. This means the stolid, safe name
"getPropertyDescriptor" is least likely to confuse.
I see what you mean about describe in the context of setting a
description (depict in a graphics context is problematic too) --
thanks. Thesaurus doesn't include mental concept filtering, dammit.
I'm sure we'll get this right, but I'm also pretty sure "getProperty"
isn't the droid we are seeking.
The ES3 spec already specifies the specific prototype of each built-in object and their instances. However, when it comes to implementing built-in objects or for that matter user defined objects there is nothing that prevents an implementation from using "invisible" intermediate prototypes for whatever purposes it finds useful. Those, of course, would not be visible to a getPrototypeOf function.
Penetrating an implementation encapsulation barrier whether at the "engine", library, or framework level always carries with it the risk of introducing implementation dependencies. That doesn't mean there aren't good reasons for doing so, you just better know what you're doing and use appropriate care. The static Object methods are intend for use by people who know what they are doing. Certainly, there is an attractive nuisance factor that will get some people into trouble. If you file off all the points and dull all the edges you usually do end up with a very useful tool.
Just wait, "reify" may yet end up as the last name standing...
On Jul 16, 2008, at 2:36 PM, Allen Wirfs-Brock wrote:
Just wait, "reify" may yet end up as the last name standing...
Methods don't reify things, the language definition does. Property
descriptors are reified in ES3.1 whether or not you ever call the
method.
I think getPropertyDescriptor is the best name suggested so far, it
has no chance of being confused for a method that would get the
property value, and it does not use obscure CS jargon in an incorrect
way. I don't think brevity is critical for these metaprogramming/
reflection type methods - they are not the kind of thing that will be
commonly used by most programmers. Mostly they will be used by
frameworks such as Ajax libraries or secure language subsets.
, Maciej
As far as I can recall, we didn't discuss a specific formulation that corresponds to Object.extend but we have considered (and arguably provided) pretty much equivalent functionality in our proposal. I assume that at least Doug, Adam, or Kris were specifically aware of Object.extend and would have broad it up if it was relevant. One reason, it probably wasn't was that the starting point of our design was the full reification and control of properties and their attributes rather than just copying properties. By the time we got around to cloning/copying issues we already had establish some core elements of our overall design.
Doing a bit of search I've found several different variants of the extend function. Some are defined on Object, some on Object.prototype. Some use a single source object and some use multiple source objects. What they all seem to have in common is that they copy the enumerable methods from one (or more) object to another.
The most common use case seems to be the one where the target object is a newly instantiated object without any properties of its own. That use case (at least for variants of extend that only take a single source object) is most directly supported by the Object.clone function in our proposal. However, Object.clone is defined to be a more comprehensive object duplication process than is performed by extend. It duplicates all own properties and their attributes and any internal properties such as its [[Value]] property if it has one.
I have personally considered whether there should be some sort of mechanism to filter the properties copied by Object.clone. For example, you might only copy non getter/setter properties, or only enumerable properties, or perhaps filter out ReadOnly properties. However, I never proposed any of these for the ES3.1 spec. because I have yet to find a use case that was sufficiently compelling or pervasive enough to justify making the interface to Object.clone more complex (in contrast, see the explanation in the rationale document for why we added a second argument to Object.create). If you want to do that sort of filtering you can do it using Object.wontbecalledgetProperty and Object.defineProperty. If you just want a fast and comprehensive copy use Object.clone.
The other obvious use case would seem to be adding some "mix-in" behavior to an object (some of the descriptions of extend on the web call this "inheritance" but it's not how I'd use that term). This use case is fairly directly supported by Object.defineProperties although it is formulated somewhat differently.
As I mention in our rationale document, this design isn't just a set of individual functions but an attempt at a unified design where we have tried to distribute the functional elements across of set of related functions that often have multiple uses. Object.extend is a fine function, particular when viewed from the perspective of what can be accomplished using the available ES3 APIs. However, it isn't something I would simply add as whole cloth to the set of functions we have already worked out. That would mostly just added redundant functionality and in a manner that wasn't particularly consistent with the other functions we have defined. Instead, if we added it we would potentially refactor the functionality of all of the proposed static Object functions to make them stand together as a unit. I'd be happy to discuss additional use cases to see try to see if we can find any significant hole in our proposal.
Finally, I want to say that my approach to a situation like this where there appears to be multiple versions of a similar but not identical function is not necessarily to pick one and make everybody else conform. Instead, I like to approach the problem from the perspective of what would have made these various functions unnecessary and what primitives would have been useful in implementing the assorted variations. If I can provide that then future users are unlikely to need to use the old forms and existing user can migrate by continuing to use their old API but perhaps reimplementing them using the new primitives.
On Jul 16, 2008, at 4:10 PM, Allen Wirfs-Brock wrote:
The most common use case seems to be the one where the target object
is a newly instantiated object without any properties of its own.
That use case (at least for variants of extend that only take a
single source object) is most directly supported by the Object.clone
function in our proposal. However, Object.clone is defined to be a
more comprehensive object duplication process than is performed by
extend. It duplicates all own properties and their attributes and
any internal properties such as its [[Value]] property if it has one.
-
It seems like Object.clone as you have described it is not suitable
for the "mixin" type use case where an object gets properties/methods
from two others. Or at least, it only does half the job. -
Is Object.clone expected to work on host objects (in particular DOM- related objects)? I think thorough cloning of all state is not a
practical semantic in that case, and would be a very large burden on
implementations. In the case of some classes (Window or Location for
instance) allowing true cloning might even be a security risk. And if
it does not support host objects then copying internal state like the
[[Value]] or [[Class]] property for ES standard object types will be
more confusing than helpful.
, Maciej
Brendan Eich wrote:
On Jul 16, 2008, at 11:44 AM, Brendan Eich wrote:
On Jul 16, 2008, at 11:35 AM, Allen Wirfs-Brock wrote:
I could live with lookup, although I think it focuses the meaning on the access process rather than on the result. Another, slightly longer alternative would be "retrieve". What do you say to Ingvar's suggestion of "inspect"?
Or (drum roll) "describe": describeProperty, which returns a property
descriptor.
Another idea (with another drum roll): checkProperty. Really short and accurately descriptive.
Although I agree that brevity isn't very important here, still here's a shorter variation on the same names:
Object.getDescriptor (MyObject, "MyName");
Object.setDescriptor (MyObject, "MyName", {enumerable: false});
Ingvar
Object.clone is really just what is sometimes called a shallow copy. I would expect it to be most commonly used to make copies of objects that are mostly statefull, not for moving behavior around.
If you have one or more mix-ins that you want to directly add to a objects, I would use defineProperties. Generally, one defineProperties per mixin although you could also use defineProperties to compose a number of such mix-ins into a composite which you then injected into objects as a unit.
However, I don't think I would normally want to do a lot of that sort of property injection, at least not for behavioral properties. (augmenting host objects is probably an exception to that) The cheapest way to provide behavior to multiple objects should be via their prototype chains. Object.create make it easy to do that and it can also be used to linearize a set of mix-ins into a prototype chain. For example, assume that a,b, and c are property set descriptors for 3 mix-ins. You can linearize them by:
var newParent = Object.create(Object.create(Object.create({},c),b),a);
and then create instances with that behavior by doing something like: return Object.create(newParent,perInstanceProperties)
The Es3.1 draft currently says that the effect of Object.clone is implementation defined for host objects. That's because I've had it beat into me (mostly by people on this list) that host objects get to set their own rules and that there is nothing we can do about it. Personally, I'd be more demanding upon the integration of host objects but, as I can imagine Brendan saying, that ship sailed ten years ago.
2008/7/15 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:
I've up loaded to the wiki a new document titled: "Proposed ECMAScript
3.1
Static Object Functions: Use Cases and Rationale"
A couple of questions for you:
My first question: How does an ES3.1 "sealed" object relate to fixtures?
_________| ES 3.1 | ES4 Sealed object | Object.seal | fixture (var)
My second question is about Object.create. Object.create() mixes adding a prototype to an object with a specialized context where the (verbose) object literal contains certain property names, such as "value", take on contextual meaning to the property.
- What happens when an unrecognized property is in the descriptor? (1)
- Is it only possible to define attributes on the top level? (2)
(1) Object.create(myProto, { method3: { enumerable: false, writable: false, novalue : true // no [[value]] + custom attribute }, ,method2: undefined });
(2) Object.create(myProto, { o1: { enumerable: true, writable: false, value : { enumerable : false, value : undefined } } });
Garrett
On Wed, Jul 16, 2008 at 2:17 PM, Robert Sayre <sayrer at gmail.com> wrote:
Maybe someone could just give the rationale for leaving out Object.extend?
Douglas Crockford wrote that it was considered, but I'm confused since it looks like you haven't even seen a proposal, and didn't participate in the discussion to exclude it.
I do not remember any discussion of Object.extend(). However, I have not attended all ES3.1 discussions.
Arguably, some of the need for direct prototype access is alleviated by providing the clone method. However, there are still plenty of other situations where it is useful.
I observe that proto in SpiderMonkey- and Rhino-based JS is mostly used for cases covered by Object.create, with a minority use- case that we've discussed before initializing it to null in object initialisers to make maps (dictionaries).
I am curious how Object.create covers this proto use case of making objects with a defined proto. Doesn't Object.create create a new object and copy properties over? proto allows objects with existing properties to have their proto defined in constant time, but isn't Object.create still O(n), with n being the number of properties?
Kris
Object.create does not support changing the [[Prototype]] of an already instantiated object, if that was the question. It creates a new object with a caller specified [[Prototype]] value. Dynamic modification of an already instantiated object's [[Prototype]] is not something that I'm ready to advocate for. I believe that the single argument form has the same semantics as Crockford's beget function, however it need not be implemented in the same manner that Doug uses for beget.
The two argument form also adds the specified own properties to the new object. It's probably fair to say that a completely unoptimized implementation of this form would be O(n) on the number of properties. However, I believe that Object.create is highly amendable to optimization. In particular, cases with the properties descriptor is specified as an object literal consisting only of literal values (including many function expression) might be implemented to operate in near constant time. Note that the same would be true if the second argument had been specified as being an object whose properties are to be directly copied (essentially making it somewhat more similar to the Object.extend function) and the argument is specified using an object literal. However, as the use of property descriptors proves the ability to specify the attributes of the copied properties in addition to their values.
The fixture question is a good one, but takes some thought to answer well so I'm not going to tackle it until tomorrow.
The other questions are easy:
- as currently specified, unrecognized properties of a property descriptor are ignored.
- no, in your example, the value of the o1 property of the created object would be an object that looks like a property descriptor.
From: Garrett Smith [mailto:dhtmlkitchen at gmail.com] Sent: Wednesday, July 16, 2008 9:17 PM To: Allen Wirfs-Brock Cc: es4-discuss at mozilla.org Subject: Re: ES3.1 Object static methods rationale document
2008/7/15 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com<mailto:Allen.Wirfs-Brock at microsoft.com>>:
I've up loaded to the wiki a new document titled: "Proposed ECMAScript 3.1 Static Object Functions: Use Cases and Rationale"
A couple of questions for you:
My first question: How does an ES3.1 "sealed" object relate to fixtures?
_________| ES 3.1 | ES4 Sealed object | Object.seal | fixture (var)
My second question is about Object.create. Object.create() mixes adding a prototype to an object with a specialized context where the (verbose) object literal contains certain property names, such as "value", take on contextual meaning to the property.
- What happens when an unrecognized property is in the descriptor? (1)
- Is it only possible to define attributes on the top level? (2)
(1) Object.create(myProto, { method3: { enumerable: false, writable: false, novalue : true // no [[value]] + custom attribute }, ,method2: undefined });
(2) Object.create(myProto, { o1: { enumerable: true, writable: false, value : { enumerable : false, value : undefined } } });
Garrett
On Jul 16, 2008, at 10:14 PM, Kris Zyp wrote:
Arguably, some of the need for direct prototype access is alleviated by providing the clone method. However, there are still plenty of other situations where it is useful.
I observe that proto in SpiderMonkey- and Rhino-based JS is mostly used for cases covered by Object.create, with a minority use- case that we've discussed before initializing it to null in object initialisers to make maps (dictionaries).
I am curious how Object.create covers this proto use case of
making objects with a defined proto. Doesn't Object.create create a new
object and copy properties over?
ES3.1 draft dated 15-July-08:
15.2.3.6 Object.create ( O [, Properties] )
The create method creates a new object with a specified prototype.
When the static create method is called, the following steps are taken:
-
If Type(O) is not Object throw a TypeError exception.
-
Create a new object as if by the expression new
Object() where Object is the standard built-in constructor with that
name
-
Call the standard built-in function
Object.defineProperties with arguments Result(2) and Properties.
-
Set the internal [[Prototype]] property of Result
(2) to Result(1).
-
Return Result(4).
proto allows objects with existing properties to have their proto defined in constant time, but isn't Object.create
still O(n), with n being the number of properties?
Object.create allows creation of a new Object instance with a
designated prototype object initializing [[Prototype]].
I wanted to bring up some further evidence for the widespread use of an extend() method. Here are the top 5 JavaScript libraries and their associated versions of "Object.extend()":
== jQuery.js:
(jQuery also does deep extend - but that isn't relevant here.)
jQuery.extend = jQuery.fn.extend = function() { var target = arguments[0] || {}, i = 1, length = arguments.length, options;
if ( typeof target != "object" && typeof target != "function" )
target = {};
if ( length == i ) {
target = this;
--i;
}
for ( ; i < length; i++ ) {
if ( (options = arguments[ i ]) != null ) {
for ( var name in options ) {
target[ name ] = options[ name ];
}
}
}
return target;
};
== Prototype.js:
Object.extend = function(destination, source) { for (var property in source) destination[property] = source[property]; return destination; };
== Mootools.js:
var $extend = function(){ var args = arguments; if (!args[1]) args = [this, args[0]]; for (var property in args[1]) args[0][property] = args[1][property]; return args[0]; };
== Dojo Toolkit:
dojo._mixin = function(obj, props){ var tobj = {}; for(var x in props){ if(tobj[x] === undefined || tobj[x] != props[x]){ obj[x] = props[x]; } } // IE doesn't recognize custom toStrings in for..in if(d["isIE"] && props){ var p = props.toString; if(typeof p == "function" && p != obj.toString && p != tobj.toString && p != "\nfunction toString() {\n [native code]\n}\n"){ obj.toString = props.toString; } } return obj; // Object }
dojo.mixin = function(obj){ for(var i=1, l=arguments.length; i<l; i++){ d._mixin(obj, arguments[i]); } return obj; // Object }
== Yahoo UI:
YAHOO.lang.augmentObject = function(r, s) { if (!s||!r) { throw new Error("Absorb failed, verify dependencies."); } var a=arguments, i, p, override=a[2]; if (override && override!==true) { // only absorb the specified properties for (i=2; i<a.length; i=i+1) { r[a[i]] = s[a[i]]; } } else { // take everything, overwriting only if the third parameter is true for (p in s) { if (override || !(p in r)) { r[p] = s[p]; } }
L._IEEnumFix(r, s);
}
};
There are a couple points that are very important here:
- They all extend the base object with the enumerable properties at least one other object.
- There is very little done to prevent properties coming in from [SomeObject].prototype - this is mostly because libraries opt not to use .hasOwnProperty() in favor of speed and/or cross-browser compatibility (older versions of Safari and IE Mac don't have hasOwnProperty).
- A couple of the implementations take multiple source objects with which to extend the base object.
The implementations in the libraries don't deal with nearly as many edge cases as they should (such as the aforementioned hasOwnProperty - or getters and setters) which is something that can be done in a language implementation. A language implementation of .extend() should certainly also allowing non-enumerable properties to be extended, as well (considering that this wont be possible - or will be very difficult to implement - from a pure-script perspective).
While Object.clone will certainly be useful in, and of, itself - it's not a replacement for an extend method.
I have a pure-JavaScript version of Object.extend() that I'm working on - and I'm building a test suite for it, as well (to make sure all edge cases are properly defined and handled): ejohn.org/files/object-extend.js
I'll be updating this file throughout the day. I'll post back when I feel as if I have a reasonable test suite.
2008/7/16 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:
I've up loaded to the wiki a new document titled: "Proposed ECMAScript 3.1 Static Object Functions: Use Cases and Rationale"
I've got one objection to Object.getPrototypeOf: It allows inspection and modification of prototype chains established like this:
function F(){}
/* add some prototype properties here */
newObj=new F;
F.prototype={constructor:F};
In ES3.0, this allows you to create unexposed and protected prototype chains. There's no way to recover newObj.[[Prototype]] once F.prototype has been changed in ES3.0 code. Granted, I've never seen this pattern used in production code except incidentally, but it's a guarantee that ES3.0 does have nonetheless and I could see myself using it if I really wanted to protect an object's prototype from being modified.
John Resig wrote:
I wanted to bring up some further evidence for the widespread use of an extend() method.
I always need both these functions. However my ObjectClone function is just a single statement that calls the other function: return ObjectMerge (Source, {}). So I see Clone as trivial syntactic sugar. Merge is necessary and costs work and processing time.
Ingvar
On Thu, Jul 17, 2008 at 8:37 AM, John Resig <jresig at mozilla.com> wrote:
I wanted to bring up some further evidence for the widespread use of an extend() method. Here are the top 5 JavaScript libraries and their associated versions of "Object.extend()":
There are a couple points that are very important here:
- They all extend the base object with the enumerable properties at least one other object.
Not true. YAHOO.lang.extend uses prototypal inheritance.
- There is very little done to prevent properties coming in from [SomeObject].prototype - this is mostly because libraries opt not to use .hasOwnProperty() in favor of speed and/or cross-browser compatibility (older versions of Safari and IE Mac don't have hasOwnProperty).
Carelessness could be another reason. It is not hard to find bugs by looking at the latest version of YAHOO.lang.extend, for example.
Garrett
--John
[snip top post]
John,
Thanks for pulling together all the various versions of Object.extend. It's useful to have them in one place.
There are a couple of things you mentioned that I wanted to clarify.
Neither Object.create nor Object.clone was not intended to be a directly replacement for Object.extend. The more direct comparable would be Object.defineProperties. Object.create's heritage is Doug Crockford's beget function and its primary purpose is to provide a more direct way to create an object with an explicitly specified prototype. Other than for atomicity and any optimization that an implementation might be able to perform Object.create(parent,properties) is exactly equivalent to Object.defineProperties(Object.create(parent), properties). Object.clone is just a "shallow copy" and its primary use case is exact copies of objects.
Object.defineProperties is comparable to Object.extend in the sense that it injects a set of properties into an already existing object. It is different from Object.extend in that rather than copying the injected property directly from a source object, it uses a meta level description of the properties to be injected. It is more general, in that it can inject properties with any attribute settings and allows control of attribute values at the per attribute per property level.
It may be useful to think of defineProperties as defining a mechanism for copying properties but without defining a fixed policies concerning which properties to copy. In contrast, Object.extend applies a specific policy for copying.
Collectively, getOwnProperty,defineProperties/defineProperty,getOwnPropertyNames,getPrototypeOf are intended to provide all the mechanisms necessary to build any of these variants to extent or any other model of property manipulation using whatever policies for property selection that might be desired by the designer.
I'll exit with three essay questions:
Is the formulation and policies of Object.extend the ideal, or is it simply the best that could be accomplished given the available reflective capabilities of ES3 (or extended ES3 in the case of getter/setter properties)?
Are there any essential meta-level function that would be needed to implement anything like Object.extend that are missing from the proposed set of 3.1 meta operations?
Let's assume that defineProperties was available and we have a green-field situation where somebody is creating a framework that needs to use some sort of property mix-in process. Do you think its developer would still implement their own version of Object.extend and specify their mix-ins as conventional objects, or would they just use defineProperties and specify their mix-ins as property descriptor sets?
Allen Wirfs-Brock wrote:
Object.create's heritage is Doug Crockford's beget function and its primary purpose is to provide a more direct way to create an object with an explicitly specified prototype.
I think the name Object.createHeir would clarify its use better.
2008/7/18 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:
Collectively, getOwnProperty,defineProperties/defineProperty,getOwnPropertyNames,getPrototypeOf are intended to provide all the mechanisms necessary to build any of these variants to extent or any other model of property manipulation using whatever policies for property selection that might be desired by the designer.
This suggests to have a convenience method like getOwnProperties that return an array of ptoperty descriptor for the object. It would allow a user to define a simple form of the extend method as:
Object.defineProperties(destination, Object.getOwnProperties(source));
It would also allow to define Object.clone(source) precisely as
Object.create(Object.getPrototypeOf(source), Object.getOwnProperties(source));
Which in fact shows that Object.clone is in the same category as Object.extend. Both methods defines very particular arrangement of properties in the object and less fundamental than the rest sets of the methods.
, Igor
Object.getProperties and Object.getOwnProperties were part of the early design of thiss set of functions. See for example, the June 24 draft at es3.1:es3.1_proposal_working_draft
I proposed removing them, because I felt that they were starting to impinge upon the territory that might be occupied by a high-level, mirrors-style reflection model (see bracha.org/mirrors.pdf ) for ECMAScript. I would like to avoid that because I think a comprehensive mirror reflection model is something we might, over the long term, want to considering adding to ECMAScript.
You can see the potential conflicts a couple of places. As currently defined, the property descriptor and property descriptor set objects are pure data records. They have no behavioral properties. If you latter wanted to extend them to be mirror objects they would have to add methods and, at least for the property set descriptor, there would be issue with such method names conflicting with the names of reflected properties. The other place you see it is with getProperties which had been specified to return a flattened set of properties (flatten as in nothing distinguished own from inherited properties). In a generalized reflection model you would definitely what to have access to such "inheritance" information, for example by grouping properties by prototype or by tagging each individual property with inheritance information.
We ultimately concluded that the best way to think about what we are currently provide is that it is a set of primitive mechanisms that could be used to build higher level reflection facilities. If we had a strong use case we could reintroduce getOwnProperties as such a primitive, but so far it seem non-essential. Incidentally, when we removed getOwnProperties we had to add getOwnPropertyName because otherwise you won't necessarily know what properties to ask for using getOwnProperty
On Jul 18, 2008, at 9:02 AM, Allen Wirfs-Brock wrote:
We ultimately concluded that the best way to think about what we
are currently provide is that it is a set of primitive mechanisms
that could be used to build higher level reflection facilities. If
we had a strong use case we could reintroduce getOwnProperties as
such a primitive, but so far it seem non-essential. Incidentally,
when we removed getOwnProperties we had to add getOwnPropertyName
because otherwise you won't necessarily know what properties to ask
for using getOwnProperty
This trade-off comes up often with widely-targeted languages. JS is a
case in point, but since it was over-minimized at the start,
prematurely standarized, and then not evolved in the standard form
for too long, the libraries have had to make up for the over-
minimization, with inevitable gratuitous differences (minor, usually,
in the case of functions like Object.extend).
Providing minimal, low-level facilities is not necessarily the best
approach. If the audience demonstrably needs and already uses sugar,
or a clichéd composite op, the language should give the people what
they want. If the low-level primitive (call/cc) is too hard to use
for most users, at least Lars and I have argued that simpler, easier-
to-use, higher-level functionality (generators, based on Python --
just one example) wins.
If Object.extend is the common case, but as Igor shows it can be
built on (new name alert, and "own" only)
Object.getPropertyDescriptors and defineProperties, it may be fine to
provide only Object.extend and defer Object.getPropertyDescriptors.
My own view is that at this point, given the proposed API, it's
better to provide both. The low-level primitive for the uncommon
cases not yet seen or thought of, and the higher-level API for the
many libraries that roll their own equivalents.
2008/7/18 Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com>:
We ultimately concluded that the best way to think about what we are currently provide is that it is a set of primitive mechanisms that could be used to build higher level reflection facilities. If we had a strong use case we could reintroduce getOwnProperties as such a primitive, but so far it seem non-essential. Incidentally, when we removed getOwnProperties we had to add getOwnPropertyName because otherwise you won't necessarily know what properties to ask for using getOwnProperty
If the goal is to define the minimal set of functions, then just create, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor and defineProperty is enough. One do not need to provide getPropertyNames and getOwnPropertyDescriptor as the latter can be trivially implemented using Own methods and getPrototypeOf.
But of cause then everybody would invent own clone, extend etc. So I agree with Brendan that the familiar and widely used functions should also be included to standardise their semantics.
, Igor
On Fri, Jul 18, 2008 at 7:39 AM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
John,
Thanks for pulling together all the various versions of Object.extend.
Not all.
It's useful to have them in one place.
There are a couple of things you mentioned that I wanted to clarify.
Neither Object.create nor Object.clone was not intended to be a directly replacement for Object.extend.
I'm a little confused by the double negative here. Can you clarify?
Allen, John, you two seem to be top-posting to significantly bad effect. Have either you considered replying inline-style?
en.wikipedia.org/wiki/Posting_style#Inline_replying
Garrett
[snip]
Not true. YAHOO.lang.extend uses prototypal inheritance.
YAHOO.lang.extend is similar in name only - YAHOO.lang.augmentObject is the one that's actually similar to the functionality used by other code bases.
On Fri, Jul 18, 2008 at 10:56 AM, John Resig <jresig at mozilla.com> wrote:
Not true. YAHOO.lang.extend uses prototypal inheritance.
YAHOO.lang.extend is similar in name only - YAHOO.lang.augmentObject is the one that's actually similar to the functionality used by other code bases.
You're prev response seems to have come from the discussion of Object.create. Object.create, with only one argument, is the same as beget. The beget function makes a new object that inherits members from an existing object. The rationale doc fails to make mention of this.
Is YAHOO.lang.extend(Sub, Sup, extras) less similar in functionality to object.create(sub, super) than the other augmentation rountines?
I think it's worth a look.
s = new Sub;
s -> [[Prototype]] => Sub.prototype
s -> [Prototype]] [[Prototype]] Super.prototype
Object.create from "rationale doc"
Function 'getPropertyDescriptorObject' would be a fictitious method that is somehow able to get all of the properties in Sup.prototype (even the DontEnum ones), and convert that into an object that matches the "propertyDescriptor" here.
Object.create(Sub.prototype, getPropertyDescriptorObject(Sup.prototype));
s = new Sub;
s -> [[Prototype]] => Sub.prototype
s -> [Prototype]] [[Prototype]] Super.prototype
YAHOO.lang.extend seems more closely related to Object.create than any of all of the rest of the code you (John) posted up; particularly the real version of first one (jQuery):- jqueryjs.googlecode.com/svn/trunk/jquery/src/core.js
Garrett
On Wed, Jul 16, 2008 at 5:50 PM, Maciej Stachowiak <mjs at apple.com> wrote:
On Jul 16, 2008, at 4:10 PM, Allen Wirfs-Brock wrote:
[snip]
- It seems like Object.clone as you have described it is not suitable for the "mixin" type use case where an object gets properties/methods from two others. Or at least, it only does half the job.
It seems like Object.clone might create compatibility with existing code. There is already a widespread use of an Object.clone on the web.
Has this been considered?
Garrett
You're prev response seems to have come from the discussion of Object.create.
No? We've been discussing the viability of a new Object.extend() method to be introduced in ES3.1. Mozilla has offered a proposal and is looking to implement it in SpiderMonkey. I provided examples of Object.extend-like functionality in the wild and coupled it with a sample pure-JavaScript implementation and some test cases.
Only a few test cases for now - looking to extend with more: ejohn.org/files/object-extend.js
The aforementioned Object.create and YAHOO.lang.extend are a completely separate utilities which should be judged on their own merits.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss- bounces at mozilla.org] On Behalf Of John Resig
No? We've been discussing the viability of a new Object.extend() method to be introduced in ES3.1. Mozilla has offered a proposal and is looking to implement it in SpiderMonkey. I provided examples of Object.extend-like functionality in the wild and coupled it with a sample pure-JavaScript implementation and some test cases.
...
John, Just to be clear, I haven't seen your proposal and this is the first time I've seen explicit mention that you were actually going to make a proposal for the addition of Object.extend to 3.1. I thought that we were generally discussing how Object.extent related to the Object meta functions that are already in the 3.1 draft. Now, it may just be that I somehow missed the proposal when it came by, or that the intent was implicit in some of your or Brendan's posts. Either way, it just wasn't obvious. Regardless, certainly feel free to make the proposal. Ideally, for inclusion in the 3.1 spec. it should follow the 3.1 specification methodology.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss- bounces at mozilla.org] On Behalf Of Garrett Smith Sent: Friday, July 18, 2008 12:28 PM ... You're prev response seems to have come from the discussion of Object.create. Object.create, with only one argument, is the same as beget. The beget function makes a new object that inherits members from an existing object. The rationale doc fails to make mention of this.
See the first complete paragraph
-----Original Message----- From: Garrett Smith [mailto:dhtmlkitchen at gmail.com] Sent: Friday, July 18, 2008 10:31 AM ...
Neither Object.create nor Object.clone was not intended to be a directly replacement for Object.extend.
Make that: Neither Object.create or Object.clone were intended to be a direct replacement for Object.extend. Or Object.create and Object.clone were not intended to directly replace Object.extend. My brain clearly hadn't decided which formulation it was trying to spew.
The reason I would say either of the above, is that I think of both Object.create and Object.clone as primarily object instantiation functions (factories, if you prefer) and that my preference is to factor common behavioral properties (eg, methods and getters/setters) into shared "parents" on the prototype chain rather than copying them into multiple instances. Similarly, my preference for "data" properties (eg instance variable) is to define them when an object is first instantiated, rather than lazily as they are assigned to. (BTW, these preference are significantly influences by the optimization opportunities they create for a smart implementation)
To get more concrete, if I was going to implement the ECMAScript equivalent of a classic point abstraction, I would define a point parent containing all the methods. For example: const pointBehavior = Object.defineProperties({}, { add: { enumerable: false, writable: false, flexible: false, value: function (aPoint} {/function body omitted/ }}, distance: { enumerable: false, writable: false, flexible: false, value: function (aPoint} { /function body omitted/ }}, /** other methods ***/ });
And for the public point factory, I would either define an instance template object (I need to say template because the word "prototype" has too many possible interpretations in the world of ECMAScript) that gets cloned: const pointTemplate = Object.create(pointBehavior, { x: {value: 0, enumerable: true, writable: true, flexible: false}, y: {value: 0, enumerable: true, writable: true, flexible: false} }); function makePoint() {return Object.clone(pointTemplate)};
or I would just use create in the factory function: function makePoint2() { return Object.create(pointBehavior, { x: {value: 0, enumerable: true, writable: true, flexible: false}, y: {value: 0, enumerable: true, writable: true, flexible: false} }); }
or I might must inline the definition of pointBehavior into the above and get: function makePoint3() { return Object.create( Object.defineProperties({}, { //methods add: {enumerable: false, writable: false, flexible: false, value: function (aPoint} {/function body omitted/ }}, distance: {enumerable: false, writable: false, flexible: false, value: function (aPoint} {/function body omitted/ }}, /** other methods ***/ }, { //instance variables x: {value: 0, enumerable: true, writable: true, flexible: false}, y: {value: 0, enumerable: true, writable: true, flexible: false} } ); }
Now, I admit that I'm using Object.create to define the template object and that I could have used it to define the behavior object, but I rationalize that to myself as the act of instantiating a couple of singleton objects so I'm still using Object.create primarily as an instantiation function. Regardless, my sense that this style is different from what people are talking about WRT using Object.extend for injecting mix-in behaviors.
BTW, please consider the above to be part of my answer to essay question #3 that I posed in an earlier post today.
On Fri, Jul 18, 2008 at 12:40 PM, John Resig <jresig at mozilla.com> wrote:
You're prev response seems to have come from the discussion of Object.create.
No? We've been discussing the viability of a new Object.extend() method to be introduced in ES3.1.
The title of the thread is "object static methods rationale doc" and the first post has a link to the doc.
The first mention of I see of "extend" was by Brendan, who merely asked: "Did you consider prototype's Object.extend method."
To which Doug replied: "Yes we did." And Allen later replied: "As far as I can recall, we didn't discuss a specific formulation that corresponds to Object.extend"
Mozilla has offered a proposal and is looking to implement it in SpiderMonkey.
I see a bug report for that mentioned in the thread.
"Object.extend" seems to be a misnomer for augmenting objects. It might contrast with Prototype.js Object.extend. "augment" or "mixin" or even "flatten" would do the trick.
/**
- Object.create
- @description Creates a new object based on \
- subject with propertyDescriptor as the [[Prototype]]
- @param subject
- @param {PropertyDescriptor} propertyDescriptor
- properties with attributes for A's prototype */
- Object.create doesn't modify subject/receiver, extend does
- Object.create takes objects, extend takes 2 functions + 1 object descriptor
The aforementioned Object.create and YAHOO.lang.extend are a completely separate utilities which should be judged on their own merits.
And as I type this, I see now a new message from Allen: "I thought that we were generally discussing how Object.extent related to the Object meta functions that are already in the 3.1 draft."
That is what I though, too.
Garrett
On Fri, Jul 18, 2008 at 2:02 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss- bounces at mozilla.org] On Behalf Of Garrett Smith Sent: Friday, July 18, 2008 12:28 PM ... You're prev response seems to have come from the discussion of Object.create. Object.create, with only one argument, is the same as beget. The beget function makes a new object that inherits members from an existing object. The rationale doc fails to make mention of this.
See the first complete paragraph on page 13
"Note that Object.create without its optional second argument is essentially the same operation as the beget function that was been widely promoted. We (perhaps not surprisingly) agree with the utility of this function but felt that the word "beget" is probably confusing to many non-native English speakers."
Does object.create add an object to the first argument's prototype chain? That's not explicitly stated, and one has to hunt down Doug's begetObject. (below)
Does the propertyDescriptor describe an object added to the front of the object's prototype chain?
========================================================= Object.prototype.begetObject = function () { function F() {} F.prototype = this; return new F(); };
Lasse Reichstein Nielsen's "clone" function, c. 2003:- Object.prototype.clone = function () { function myConstructor = function(){}; myConstructor.prototype = this; return new myConstructor(); }
Garrett
To answer Garrent's questions I was going to directly quote the specification in the July 15 draft ES3.1 specification, but I found that that version had some issue in following the specification conventions. So here is what the definition of Object.create should be:
15.2.3.6 Object.create ( O [, Properties] ) The create method creates a new object with a specified prototype. When the static create method is called, the following steps are taken:
-
If Type(O) is not Object throw a TypeError exception.
-
Create a new object as if by the expression new Object() where Object is the standard built-in constructor with that name
-
Set the internal [[Prototype]] property of Result(2) to O.
-
Call the standard built-in function Object.defineProperties with arguments Result(2) and Properties.
-
Return Result(2).
The length property of the Object.create function is 1.
I've up loaded to the wiki a new document titled: "Proposed ECMAScript 3.1 Static Object Functions: Use Cases and Rationale"
It's available as both a pdf and as a Word doc file: es3.1:rationale_for_es3_1_static_object_methods.pdf, es3.1:rationale_for_es3_1_static_object_methods.doc