A new function name property proposal

Yehuda Katz wycats at gmail.com
Sat Nov 17 22:29:34 PST 2012


This post is about my experience with the lookup-by-reference and debug
use-cases for Function#name. Both use-cases are very general-purpose--my
examples from Ember.js are just representative examples, and illustrate
ways that we have worked around existing limitations to provide missing
functionality.

*String References*
*
*
Because of the difficulty in managing load order in current JavaScript, as
well as a desire to allow for clean dependency injection, we allow many
cross-object associations do be defined using Strings. In general, these
strings are evaluated the first time the associated object is used at
runtime, which allows dependencies to be injected and ensures that all
modules to be loaded before references are evaluated.

Here is an example of associations described in Ember Data:

App.Person = DS.Model.extend({
  comments: hasMany('App.Comment')
});

// in another module

App.Comment = DS.Model.extend({
  person: belongsTo('App.Person')
});

So how does this sort of thing work? Instead of making App a standard
JavaScript object, App is an instance of Ember.Namespace. Once we know its
name (which we do by searching on window for instances of Ember.Namespace,
gross, I know), we can easily resolve String names into actual objects at
runtime.

This solution, of course, assumes coordination across a global object,
which isn't desirable in a world with modules, and the problem may even be
fully mitigated in a world with modules. I'm not sure how well modules
handle circular dependencies--lazily evaluating strings handles the problem
elegantly by deferring evaluation of dependencies until all of the modules
have loaded.

Sencha uses a similar approach for dependencies (see
https://www.sencha.com/learn/how-to-use-classes-in-sencha-touch-2). In
particular, Sencha defines and references classes by Strings, avoiding the
reference problem. This approach requires the use of a static creation
function (Ext.create('Animal', { })), so the String semantics leak into
runtime as well.

Internally, Objective-J compiles class references to a lookup in
REGISTERED_CLASSES, which serves a similar purpose, but allows for nicer
syntax through a preprocessor.

This use-case would not be addressed by improved lexical inference of names
in functions and classes, but the final module proposal will hopefully moot
this use-case. There are probably other lookup-by-reference use-cases that
would not be handled by modules.

*Debugging*
*
*
Similarly, Ember provides a default toString that is more descriptive than
the default [object Object].

App = Ember.Namespace.create();
App.Person = Ember.Object.extend();
App.Person.toString(); // "App.Person"
var person = new App.Person();
person.toString() // "<App.Person:ember1234>"

The part after the colon is a debug-friendly object ID we lazily create
when needed. (also, until we have a reliable Set, Map and WeakMap, that
object ID is also used as the key in Objects that are used to emulate Sets,
Maps and WeakMaps).

Historically, we have tried to install these toString values as displayName.
Because Chrome does not support displayName, we eventually stopped trying
to add it. (also, our current lookup implement is lazy, like toString, and
displayName is an eager value).

For what it's worth, Chrome's attempt to guess names in lieu of support for
displayName, while better than nothing, isn't good enough in many cases and
blocks us from providing the best possible debugging experience in the
Chrome debugger. Many others have been similarly stymied, the Chrome ticket
to support displayName (
https://code.google.com/p/chromium/issues/detail?id=17356) has been open
since 2009 and has comments as recently as this year.

For this use-case, Maciej or Dave's proposal at
http://wiki.ecmascript.org/doku.php?id=strawman:name_property_of_functions
would
be helpful. I like Dave's variant (which allows specification of separate
call behavior, construct behavior and an alternative prototype to
Function.prototype). This would allow a very simple implementation of
imperative class-side inheritance, among other things. I know this can be
achieved via proxies and/or __proto__, but this is much nicer.

In general, I like the idea of inferring useful information statically. The
one caveat is that libraries creating class-like abstractions usually
create constructors inside of the implementation. In Ember's case (see
http://jsbin.com/ijicor/78), this means that the statically inferred name
is always Class. Interestingly (and usefully), Firefox uses the provided
toString value in its output.

In a similar Backbone example (see http://jsbin.com/uqanem/1), Chrome
infers d, and prints d plus a set of internal attributes. Firefox just
prints the internal attributes.

Of course, in the long run, many of these non-transpiled use cases would be
handled by a robust and broadly implemented declarative class syntax.
Transpiled use cases (like the original case, Objective-J) would probably
continue to benefit from more control over the name.

*TL;DR*
*
*
I would really like to see Dave's Function.create(name, call, construct,
proto) proposal come to pass. Fleshing out static name extraction as
described above would be a big benefit to a lot of common uses, and I am in
favor of it. Direct control of the name, not static inference, is needed
for imperative class-like abstractions.

On Sat, Nov 17, 2012 at 7:44 PM, Brandon Benvie
<brandon at brandonbenvie.com>wrote:

> Excellent! I must admit to having a pretty ridiculous obsession with the
> name property of functions in particular, so I would be much delighted to
> see improvements and standardization come to it.
>
>
> On Sat, Nov 17, 2012 at 9:06 PM, Brendan Eich <brendan at mozilla.org> wrote:
>
>> Brandon: thanks for reviving and revising my old strawman. I will edit
>> your proposed changes in (and let you and the list know when I'm done so
>> you can review) and present at the TC39 meeting in the last week of
>> November. Thanks again, great work!
>>
>> /be
>>
>> Brandon Benvie wrote:
>>
>>> Declarative scope...not decorative scope. Curses auto correct fail.
>>> ______________________________**_________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/**listinfo/es-discuss<https://mail.mozilla.org/listinfo/es-discuss>
>>>
>>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>


-- 
Yehuda Katz
(ph) 718.877.1325
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121117/e62a65f1/attachment.html>


More information about the es-discuss mailing list