Reputation: 147
My new job is to write component-oriented JavaScript with Google Closure library. I adore events, components, services and modules. But the work is super-harsh because of the need to write code cluttered with namespaces. Following code is typical:
goog.provide(com.bin.slash.dot.closure.widget.SuperForm);
goog.require(com.bin.slash.dot.closure.widget.Avatar);
// ... ten require calls more...
com.bin.slash.dot.closure.widget.SuperForm = function() {
goog.base(this);
this._internal = new com.bin.slash.dot.closure.widget.Avatar(
com.bin.slash.dot.closure.widget.Avatar.SRC_PATH);
};
And I can't believe this is true. I am not afraid to type all of this, but I just feel that logic dissolved and cluttered in this symbol hell. It is very difficult to scan, hence it takes more time to understand what'is going on. My boss said, that it is discouraged to write shortcuts like:
var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};
Because all of them will be bound to the global namespace (window) after compilation, so they can interfere with something else.
The question is how to avoid this symbol hell?
Update: I have made an improvement into my developer process, which solves the symbol hell. Now I write sweetened JavaScript, that then automatically compiled by Grunt with sweet.js macros:
// For each file I define three macros which are replaced
// in the compile time with hell of a long paths.
macro dir { rule { $x } => { my.very.very.long.namespace $x } }
macro class { rule { $x } => { dir.NameOfMyClass $x } }
macro proto { rule { $x } => { class.prototype $x } }
dir.NameOfMyClass = function() {}; // yields: my.very.very.long.namespaceNameOfMyClass = function() {};
class.CONSTANT = "I don't know why we write constants into classes, not prototypes"; // yields: my.very.very.long.namespaceNameOfMyClass.CONSTANT = ...;
proto.method1 = function() {}; // yields my.very.very.long.namespaceNameOfMyClass.prototype.method1 = function(){};
All of the noise created by macro compiler is removed by excellent shelljs.
Upvotes: 3
Views: 2955
Reputation: 517
Assuming you're using the Closure Compiler, consider goog.scope
. There is built-in compiler support that replaces the aliased variables before optimization:
goog.scope
– a design document from the Closure Library wikigoog.scope
goog.scope
goog.provide
and goog.require
syntax is unaltered;Your code example, taking the above guide into consideration, could look somewhat like this:
goog.provide('my.very.long.namespace.NameOfMyClass');
goog.require('my.very.long.namespace');
/** @constructor */
my.very.long.namespace.NameOfMyClass = function() { /*...*/ };
goog.scope(function() {
var _ = my.very.long.namespace.NameOfMyClass;
_.CONSTANT = 'I don\'t know why we write constants into classes, not prototypes';
_.prototype.method1 = function() {};
}); // goog.scope
Since I don't have enough reputation to add a comment:
My boss said, that it is discouraged to write shortcuts like:
var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};
Because all of them will be bound to the global namespace (window) after compilation, so they can interfere with something else.
Even in the absence of those shortcuts, advanced compilation can rename symbols such as SF
or myVariable
to simply ga
. This can lead to clashes with external code such as Google Analytics.
The Closure-supported way to prevent such clashes on the global scope is introducing an immediately-invoked function expression after compilation (source). Use the compiler flag: --output_wrapper "(function(){%output%})();"
, or the strict mode compliant variation: --output_wrapper "(function(){%output%}).call(this);"
. When used, the shortcuts discouraged by your boss will be safe from collision with external symbols.
Upvotes: 6
Reputation: 8053
What about creating separate local context using immediate function:
(function () {
var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};
}());
Your code should be put only inside this function. This way you will never overwrite any global variable.
Upvotes: 1
Reputation: 2278
There is nothing wrong with defining references to simplify your workflow IMO.
var
widget = com.bin.slash.dot.closure.widget
widget.methA = function(){ widget.propertyA = 10}
Namespacing serves a purpose and while I can't speak on your codebase, chances are there are better ways to organize this library.
Upvotes: 1