Ext.data.JsonP.command_code({"title":"Compiler-Friendly Code Guidelines","guide":"
Contents
\n\nOne of the major components new to V3 of Sencha Cmd is the compiler. This guide tells you\nall about how to write code that gets the most out of the compiler today, but also to help\nyour code base immediately benefit from the framework-aware optimizations that we have\nplanned for future releases.
\n\nFor starters, it is important to explain that th Sencha Cmd compiler is not a replacement\nfor tools like these:
\n\n\n\n\nThese tools solve different problems for JavaScript developers in general. These tools are\nvery good at the world of JavaScript but have no understanding of Sencha framework features\nsuch as Ext.define
for declaring classes.
The role of the Sencha Cmd compiler is then to provide framework-aware optimizations and\ndiagnostics. Once code has passed through the Sencha Cmd compiler, it is ready to go on\nto more general tools like the above.
\n\nIn early testing, these kinds of optimizations have shown to significantly improve \"ingest\"\ntime of JavaScript code by the browser... especially on legacy browsers.
\n\nIn order for the compiler to provide these benefits, however, it is now important to look\nat coding conventions that it can \"understand\" and therefore optimize for you. Following\nthe conventions described in this guide will ensure that your code is positioned to get\nthe most from Sencha Cmd today and in the future.
\n\nThe dynamic loader and the previous JSBuilder have always made certain assumptions about\nhow classes are organized, but they were not seriously impacted by failure to follow those\nguidelines. These guidelines are very similar to Java.
\n\nTo recap, these guidelines are:
\n\nExt.define
statement at global scope.Ext.define(\"MyApp.foo.bar.Thing\", ...
would be\n\"Thing.js\".Ext.define(\"MyApp.foo.bar.Thing\", ...
, the source file\nwould be in a path ending with \"/foo/bar\".Internally, the compiler views source files and classes as basically synonymous. It makes\nno attempt to split up files to remove classes that are not required. Only complete files\nare selected and included in the output. This means that if any class in a source file is\nrequired, all classes in the file will be included in the output.
\n\nTo give the compiler the freedom to select code at the class-level, it is essential to put\nonly one class in each file.
\n\nThe Sencha Class System provides the Ext.define
function to enable higher-level, object\noriented programming. The compiler takes the view that Ext.define
is really a form of\n\"declarative\" programming and processes the \"class declaration\" accordingly.
Clearly if Ext.define
is understood as a declaration, the content of the class body cannot\nbe constructed dynamically in code. While this practice is rare, it is valid JavaScript.\nBut as we shall see below in the code forms, this is antithetical to the compiler's\nability to understand the code is parses. Dynamic class declarations are often used to do\nthings that can better be handled by other features of the compiler. For more on these\nfeatures, see the Sencha Compiler Reference.
The compiler understands the \"keywords\" of this declarative language:
\n\nrequires
uses
extend
mixins
statics
alias
override
alternateClassName
xtype
In order for the compiler to recognize your class declarations, they need to follow one of\nthe following forms.
\n\nMost classes use simple declarations like this:
\n\nExt.define('Foo.bar.Thing', {\n // keywords go here ... such as:\n\n extend: '...',\n\n // ...\n});\n
\n\nThe second argument is the class body which is processed by the compiler as the class\n\"declaration\".
\n\n In all forms, Ext.define
should be called at global scope.
In some use cases the class declaration is wrapped in a function to create a closure scope\nfor the class methods. In all of the various forms, it is critical for the compiler that\nthe function end with a return
statement that returns the class body as an object\nliteral. Other techniques are not recognized by the compiler.
To streamline the older forms of this technique described below, Ext.define
understands\nthat if given a function as its second argument, that it should invoke that function to\nproduce the class body. It also passes the reference to the class as the single argument\nto facilitate access to static members via the closure scope. Internally to the framework,\nthis was the most common reason for the closure scope.
Ext.define('Foo.bar.Thing', function (Thing) {\n\n return {\n // keywords go here ... such as:\n\n extend: '...',\n\n // ...\n };\n});\n
\n\nNOTE: This form is only supported in Ext JS 4.1.2 and Sencha Touch 2.1 (or newer).
\n\nIn previous releases, the \"Function Form\" was not supported, so the function was simply\ninvoked immediately:
\n\nExt.define('Foo.bar.Thing', function () {\n\n return {\n // keywords go here ... such as:\n\n extend: '...',\n\n // ...\n };\n}());\n
\n\nThis form and the next are commonly used to appease tools like JSHint (or JSLint).
\n\nExt.define('Foo.bar.Thing', (function () {\n\n return {\n // keywords go here ... such as:\n\n extend: '...',\n\n // ...\n };\n})());\n
\n\nAnother variation on immediately called \"Function Form\" to appease JSHint/JSLint.
\n\nExt.define('Foo.bar.Thing', (function () {\n\n return {\n // keywords go here ... such as:\n\n extend: '...',\n\n // ...\n };\n}()));\n
\n\nThe class declaration in its many forms ultimately contains \"keywords\". Each keyword has\nits own semantics, but there are many that have a common \"shape\".
\n\nThe extend
and override
keywords only accept a string literal.
These keywords are also mutually exclusive in that only one can be used in any declaration.
\n\nThe following keywords all have the same form:
\n\nrequires
uses
alias
alternateClassName
xtype
The supported forms for these keywords are as follows.
\n\nJust a string:
\n\nrequires: 'Foo.thing.Bar',\n//...\n
\n\nAn array of strings:
\n\nrequires: [ 'Foo.thing.Bar', 'Foo.other.Thing' ],\n//...\n
\n\nmixins
Using an object literal, the name given the mixin can be quoted or not:
\n\nmixins: {\n name: 'Foo.bar.Mixin',\n 'other': 'Foo.other.Mixin'\n},\n//...\n
\n\nMixins can also be specified as a String[]:
\n\nmixins: [\n 'Foo.bar.Mixin',\n 'Foo.other.Mixin'\n],\n//...\n
\n\nstatics
KeywordThis keyword is used to place properties or methods on the class as opposed to on each of\nthe instances. This must be an object literal.
\n\nstatics: {\n // members go here\n},\n// ...\n
\n\nsingleton
KeywordThis keyword was historically only used with a boolean \"true\" value:
\n\nsingleton: true,\n
\n\nThe following (redundant) use is also supported:
\n\nsingleton: false,\n
\n\nIn Ext JS 4.1.0 and Sencha Touch 2.0, Ext.define
gained the ability to manage overrides.\nHistorically, overrides have been used to patch code to work around bugs or add\nenhancements. This use was complicated with the introduction of the dynamic loader because\nof the timing required to execute the Ext.override
method. Also, in large applications\nwith many overrides, not all overrides in the code base were need by all pages or builds\n(e.g., if the target class was not required).
All this changed once the class system and loader understood overrides. This trend only\ncontinues with Sencha Cmd. The compiler understands overrides and their dependency effects\nand load-sequence issues.
\n\nIn the future, the compiler will become even more aggressive at dead-code elimination of\nmethods replaced by an override. Using managed overrides as described below will enable\nthis optimization of your code once available in Sencha Cmd.
\n\nBelow is the standard form of an override. The choice of namespace is somewhat arbitrary,\nbut see below for some suggestions.
\n\nExt.define('MyApp.patches.grid.Panel', {\n override: 'Ext.grid.Panel',\n\n ...\n});\n
\n\nWith the ability to use Ext.define
to manage overrides, new idioms have opened up and\nare actively being leveraged. For exampled in the code generators of\nSencha Architect and internal to the framework,\nto break apart large classes like Ext.Element
into more manageable and cohesive pieces.
This is the historical use case and hence the most common in practice today.
\n\nCAUTION: Care should be taken when patching code. While the use of override itself is\nsupported, the end result of overriding framework methods is not supported. All overrides\nshould be carefully reviewed whenever upgrading to a new framework version.
\n\nThat said, it is, at times, necessary to override framework methods. The most common case\nfor this to fix a bug. The Standard Override Form is ideal in this case. In fact, Sencha\nSupport will at times provide customer with patches in this form. Once provided, however,\nmanaging such patches and removing them when no longer needed is a matter for the review\nprocess previously mentioned.
\n\nNaming Recommendation:
\n\nOrganize patches in a namespace associated with the top-level namespace of the target.\nFor example, \"MyApp.patches\" targets the \"Ext\" namespace. If third party code is involved\nthen perhaps another level or namespace should be chosen to correspond to its top-level\nnamespace. From there, name the override using a matching name and sub-namespace. In the\nabove example:
\n\n(Ext -> MyApp.patches).grid.Panel
When dealing with code generation (as in Sencha Architect), it is common for a class to\nconsist of two parts: one machine generated and one human edited. In some languages, there\nis formal support for the notion of a \"partial class\" or a class-in-two-parts.
\n\nUsing an override, you can manage this cleanly:
\n\nIn ./foo/bar/Thing.js:
\n\nExt.define('Foo.bar.Thing', {\n // NOTE: This class is generated - DO NOT EDIT...\n\n requires: [\n 'Foo.bar.custom.Thing'\n ],\n\n method: function () {\n // some generated method\n },\n\n ...\n});\n
\n\nIn ./foo/bar/custom/Thing.js:
\n\nExt.define('Foo.bar.custom.Thing', {\n override: 'Foo.bar.Thing',\n\n method: function () {\n this.callParent(); // calls generated method\n ...\n },\n\n ...\n});\n
\n\nNaming Recommendations:
\n\nA common problem for base classes in object-oriented designs is the \"fat base class\". This\nhappens because some behaviors apply across all classes. When these behaviors (or features)\nare not needed, however, they cannot be readily removed if they are implemented as part of\nsome large base class.
\n\nUsing overrides, these features can be collected in their own hierarchy and then requires
\ncan be used to select these features when needed.
In ./foo/feature/Component.js:
\n\nExt.define('Foo.feature.Component', {\n override: 'Ext.Component',\n\n ...\n});\n
\n\nIn ./foo/feature/grid/Panel.js:
\n\nExt.define('Foo.feature.grid.Panel', {\n override: 'Ext.grid.Panel',\n\n requires: [\n 'Foo.feature.Component' // since overrides do not \"extend\" each other\n ],\n\n ...\n});\n
\n\nThis feature can be used now by requiring it:
\n\n...\nrequires: [\n 'Foo.feature.grid.Panel'\n]\n
\n\nOr with a proper \"bootstrap\" file (see Workspaces in Sencha Cmd\nor Single-Page Ext JS Apps):
\n\n...\nrequires: [\n 'Foo.feature.*'\n]\n
\n\nNaming Recommendation:
\n\nrequires
and uses
in an OverrideThese keywords are supported in overrides. Use of requires
may limit the compiler's\nability to reorder the code of an override.
callParent
and callSuper
To support all of these new uses cases, callParent
was enhanced in Ext JS 4.0 and Sencha\nTouch 2.0 to \"call the next method\". The \"next method\" may be an overridden method or an\ninherited method. As long as there is a next method, callParent
will call it.
Another way to view this is that callParent
works the same for all forms of Ext.define
,\nbe they classes or overrides.
While this helped in some areas, it unfortunately made bypassing the original method (as a\npatch or bug fix) more difficult. So in Ext JS 4.1.1a, Ext JS 4.1.2a and Sencha Touch 2.1,\nthere is now a method named callSuper
that can be used to bypass an overridden method.
In future releases, the compiler will use this semantic difference to perform dead-code\nelimination of overridden methods.
\n\nAs Sencha Cmd continues to evolve, it will continue to introduce new diagnostic messages\nto help point out deviations from these guidelines.
\n\nA good place to start is to see how this information can help inform your own internal\ncode style guidelines and practices.
\n"});