[Dojo-interest] dojo.declare is landed in the trunk

46 views
Skip to first unread message

Eugene Lazutkin

unread,
Sep 17, 2009, 4:16:50 AM9/17/09
to dojo dev., dojo-i...@mail.dojotoolkit.org
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

See *Subj*.

It is supposed to a drop-in replacement, but the original dojo.declare
had many undocumented corners. Please run your code against the trunk
and see if it works as it did before. I ran our tests and as far as I
can tell everything is fine. But please double-check me! Report any
found problem here: http://bugs.dojotoolkit.org/ticket/9862

The blog post with gory details and stats and the documentation update
is forthcoming. For now you can read the inline documentation (at the
end of the file).

What is the difference with the old dojo.declare?

1) Speed. As a preview of the upcoming blog post these are some
numbers for popular browsers. IE6 is Internet Explorer 6 on Windows,
IE8 is Internet Explorer 8 on Window, S is Safari 4 on Mac, FF is
Firefox 3.5 on Linux --- all are the latest versions with the latest
patches.

Creating a class (single inheritance): IE6 - 66% faster, IE8 - 75-130%
faster (roughly twice), S - 30-40% faster, FF - 100% faster (twice).

Creating an instance (depends on a number of constructors to run): IE6
- - 0-230% faster, IE8 - 0-250% faster, S - 10-140% faster, FF - 15-155%
faster.

Calling inherited methods (depends on the depth of a chain): IE6 -
30-90% faster, but in one unremarkable case I found it to be 10%
slower (most probably a problem with time measurements), IE8 - 0-50%
faster, S - ~100% faster, FF - 20-150% faster.

2) Several bugs are fixed.

3) More comprehensive error messages.

4) Better structured and better commented unobfuscated code.

5) Implements C3 MRO algorithm to support true multiple inheritance
via the linearization of classes, which helps with corner cases. The
best article on this algorithm:
http://www.python.org/download/releases/2.3/mro/ Yes, the dreaded
"diamond" case works just fine now:

dojo.declare("A", null, {...});
dojo.declare("B", A, {...});
dojo.declare("C", A, {...});
dojo.declare("D", [B, C], {...});

D will be linearized as expected: D => C => B => A, so class
precedence is preserved properly.

6) New functionality: anonymous/local classes.

If you don't want a global name for you class, do not specify it:

// old style is supported
dojo.declare("A", null, {...});
dojo.declare("B", A, {...});

// new style
var A = dojo.declare(null, {...});
var B = dojo.declare(A, {...});

7) New functionality: "raw classes" can be used as bases, their
methods can be called using this.inherited():

// "raw class"
var X = function(){...};
X.prototype = {...};

// "raw class"
var Y = function(){...};
Y.prototype = {...};

// you can base on it
dojo.declare("A", X, {...});

// you can mix it in
dojo.declare("B", [A, Y], {...});

8) New functionality: special methods, like toString() are not special
anymore --- they can be overridden even on IE (something that the old
dojo.declare couldn't do) and even called using this.inherited().

9) New functionality: this.getInherited() --- works similarly to
this.inherited() but doesn't execute the next method returning it
instead. It is useful in complex cases, callbacks and so on.

10) New functionality: this.isInstanceOf() --- emulates operator
"instanceof" returning "true" if this object supports the
class/interface in questions.

11) New functionality: dojo.makeDeclare() --- the ability to create
your own custom dojo.declare-like functions. It supports the creation
of automatically chained methods. Probably an example from the inline
documentation will explain it better:

var decl = dojo.makeDeclare(false, {
constructor: "after",
destroy: "before"
});

The first parameter turns off the legacy mode (processing preamble()
methods). Next we specify that "constructor" of a class should be
*after* "constructor" of its base, and "destroy" should be called in
the opposite order (*before* its base). Kind of like AOP terminology.

Let's create classes and an instance:

var A = decl(null, {
constructor: function(){ console.log("constructing A"); },
destroy: function(){ console.log("destroying A"); }
});
var B = decl(A, {
constructor: function(){ console.log("constructing B"); },
destroy: function(){ console.log("destroying B"); }
});
var C = decl(B, {
constructor: function(){ console.log("constructing C"); },
destroy: function(){ console.log("destroying C"); }
});

var c = new C();
c.destroy();

It will print:

constructing A
constructing B
constructing C
destroying C
destroying B
destroying A

Why is it useful? Mostly for life-cycle-related methods and for event
processing methods. It removes the need to call all methods with this
name in correct order. And it is fast. I compared the speed of
emulating the same chaining with an explicit this.inherited() call
using old dojo.declare and I got this: IE6 - 100-130% faster, IE8 -
95-150% faster, S - 10-11 *times* faster, FF - 100-300% faster. The
smallest increase was on IE, the biggest on Chrome 4 (Mac) - 13-18
times faster.

12) A bunch of tests and benchmarks is written. They will be ported
over to our declare tests.

============

What is the price of this? New dojo.declare code is ~800 bytes longer
than the old dojo.declare (built as ususal and gzipped). If we need
to, I can reduce this difference, and possibly eliminate it completely
at expense of more obfuscated but shorter code. The same goes for
speed --- there are reserves to make certain operations even faster.

The code faithfully emulates some undocumented quirks of the original
dojo.declare at expense of code size and speed: "nom", "preamble", and
"postscript". It would be nice to remove "preamble" completely (we
don't use it in our code, only in 2-3 tests, which test ...
"preamble"). And it would be nice to disclose "nom" and "postscript"
in the documentation. If anybody has thoughts on this matter, please
chime in.

Thanks,

- --
Eugene Lazutkin
http://lazutkin.com/

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkqx8G0ACgkQY214tZwSfCtPWgCeMzeJWT5EvEbo7H1GiUx4ORjM
DtkAn2bI3+RlcXlILYkiInSnb0f0fbD8
=N3J4
-----END PGP SIGNATURE-----

_______________________________________________
FAQ: http://dojotoolkit.org/support/faq
Book: http://docs.dojocampus.org
Dojo-i...@mail.dojotoolkit.org
http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest

Alex Russell

unread,
Sep 17, 2009, 4:56:08 AM9/17/09
to dojo dev., dojo-i...@mail.dojotoolkit.org
This is *wonderful* news. Great work! The 800 bytes are absolutely
worth the speed difference, IMO, and should pay off big in Dijit.

Regards

> dojo-contributors mailing list
> dojo-con...@mail.dojotoolkit.org
> http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors

--
Alex Russell
sligh...@google.com
al...@dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723

Jose Noheda

unread,
Sep 17, 2009, 5:06:28 AM9/17/09
to dojo-i...@mail.dojotoolkit.org
On Thu, Sep 17, 2009 at 10:56 AM, Alex Russell <al...@dojotoolkit.org> wrote:
This is *wonderful* news. Great work! The 800 bytes are absolutely
worth the speed difference, IMO, and should pay off big in Dijit.

Which will be really welcome! Congrats

Shane O'Sullivan

unread,
Sep 17, 2009, 5:43:21 AM9/17/09
to dojo-i...@mail.dojotoolkit.org
This is great news. Maybe I'm just over enthusiastic, but I see a
noticable difference, especially in loading large pages, e.g.
http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/widget/tests/test_PortletInGridContainer.html

Do we have stats on what percentage of time was taken up with the old
declare() on large dijit pages? It would be good PR to say our
widgets load x% faster.

Shane

2009/9/17 Jose Noheda <jose....@gmail.com>:

seth lytle

unread,
Sep 17, 2009, 10:47:53 AM9/17/09
to dojo-i...@mail.dojotoolkit.org
great write-up of the changes and trade-offs eugene - that's true
openness, disclosing not just the source code, but the thought process
and the analysis. and i'll vote in favor of normalizing preamble, nom
and postscript

seth/nqzero

>>> >
>>> > What is the difference with the old dojo.declare?
>>> >

sam foster

unread,
Sep 17, 2009, 12:19:27 PM9/17/09
to dojo-i...@mail.dojotoolkit.org
Sounds great.
FWIW I've used preamble some when using dojo/method script blocks to
bootstrap before the main widget/class lifecycle kicks off. This page
has an example: http://sitepen.com/labs/code/dirindex/data/

There might be other better ways to do the same thing -then and now-
my point is just that preamble was a feature of dojo.declare, even if
it wasnt well documented, it wasnt marked as private in any way and
has been fair game for use since 0.9 I believe. Removing it is a
backwards-compat issue.

/Sam

On Thu, Sep 17, 2009 at 9:16 AM, Eugene Lazutkin <eug...@lazutkin.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>

> The code faithfully emulates some undocumented quirks of the original
> dojo.declare at expense of code size and speed: "nom", "preamble", and
> "postscript". It would be nice to remove "preamble" completely (we
> don't use it in our code, only in 2-3 tests, which test ...
> "preamble"). And it would be nice to disclose "nom" and "postscript"
> in the documentation. If anybody has thoughts on this matter, please
> chime in.

Robert

unread,
Sep 30, 2009, 3:57:37 PM9/30/09
to dojo-i...@mail.dojotoolkit.org
Eugene Lazutkin wrote:
> 11) New functionality: dojo.makeDeclare() --- the ability to create
> your own custom dojo.declare-like functions. It supports the creation
> of automatically chained methods. Probably an example from the inline
> documentation will explain it better:
>
> var decl = dojo.makeDeclare(false, {
> constructor: "after",
> destroy: "before"
> });
>
>

If I do not specify the constructor as a chained method, can I then use
this.inherited to pass any arguments I like to the super constructor?
If so then this is great!

Eugene Lazutkin

unread,
Sep 30, 2009, 4:25:46 PM9/30/09
to dojo-i...@dojotoolkit.org
Yes, this should do the trick (assuming no chaining for other methods):

var decl = dojo.makeDeclare(false);

Please be aware that the way to do it can change before we ship 1.4.
Following the productive discussion with Mike Wilson and Scott J. Miles
I am most likely to retire makeDeclare() in favor of flags in
dojo.declare(), while preserving new and old functionality. Something
like that will replace it (possible syntax):

var A = dojo.declare(null, {
constructor: function(){
this.inherited(arguments);
...
},
"chain:ctor": false,
...
"chain:after": ["method1"],
"chain:before": ["method2"],
...
};

"chain:after" and "chain:before" are additive flags.

The point is either way you can turn-off auto-chaining of the constructor.

Thanks,

Eugene Lazutkin
Dojo Toolkit, Committer
http://lazutkin.com/

Reply all
Reply to author
Forward
0 new messages