Section 10.2. Importing Symbols from Namespaces


10.2. Importing Symbols from Namespaces

The problem with unique namespace names such as com.davidflanagan.Class is that they lead to even longer function namese.g., com.davidflanagan.Class.define( ). This is the fully qualified named of the function, but you don't have to type it this way all the time. Since JavaScript functions are data, you can put them where you want. After loading the com.davidflanagan.Class module, for example, a user of that module might write the following:

 // This is an easier name, to save typing. var define = com.davidflanagan.Class.define; 

It is the module developer's responsibility to use namespaces to prevent collisions. But it is the module user's prerogative to import symbols from the module's namespace into the global namespace. The programmer using the module will know what other modules are in use and what all the potential name collisions are. She can determine what symbols to import and how to import them to prevent collisions.

Note that the previous code snippet uses the global symbol define for a class-definition utility function. This is not a good choice for a global function because it doesn't say what is being defined. An alternative is to change the name:

 var defineClass = com.davidflanagan.Class.define; 

But changing method names is not fully satisfactory, either. Another programmer who has used the module before might find the name defineClass( ) confusing because he is familiar with the function under the name define( ). Also, module developers often put quite a bit of thought into their function names, and changing these names may not do justice to the module. Another alternative is not to use the global namespace but to import symbols into an easier-to-type namespace:

 // Create a simple namespace. No error checking required.  The // module user knows that this symbol does not exist yet. var Class = {}; // Now import a symbol into this new namespace. Class.define = com.davidflanagan.Class.define; 

There are some important things to understand about importing symbols like this. First: you can import only symbols that refer to a function, object, or array. If you import a symbol whose value is a primitive type such as a number or a string, you simply get a static copy of the value. Any changes to the value occur in the namespace and are not reflected in an imported copy of the value. Suppose that the Class.define( ) method keeps track of the number of classes it has defined and increments the value com.davidflanagan.Class.counter each time it is called. If you attempt to import this value, you merely get a static copy of its current value:

 // Make a static copy only. Changes in the namespace are not // reflected in the imported property since this is a primitive value. Class.counter = com.davidflanagan.Class.counter; 

The lesson for module developers is if your module defines properties that refer to primitive values, you should provide accessor methods that can be imported:

 // A property of primitive type; cannot be imported com.davidflanagan.Class.counter = 0; // Here is an accessor method that can be imported com.davidflanagan.Class.getCounter = function( ) {     return com.davidflanagan.Class.counter; } 

The second important point to understand about imports is that they are for the users of a module. Module developers must always use the fully qualified name of their symbols. You can see this in the getCounter( ) method just shown. Since JavaScript has no built-in support for modules and namespaces, there are no shortcuts here, and you must type the fully qualified name of the counter property even though that property and the getCounter( ) accessor method are both part of the same namespace. Module writers must not assume that their functions will be imported into the global namespace. Functions that call other functions in the module must use their fully qualified name so that they work correctly even when invoked without having been imported. (An exception to this rule, involving closures, is discussed in Section 10.2.2.)

10.2.1. Public and Private Symbols

Not all the symbols defined in a module's namespace are intended for external use. A module may have its own internal functions and variables that are not intended to be used directly by the scripts that use the module. JavaScript does not have any way to specify that some properties in a namespace are public and that some are not, and so, again, we rely on convention to prevent inappropriate use of a module's private properties from outside the module.

The most straightforward approach is simple documentation. The module developer should clearly document which functions and other properties make up the public API of the module. Conversely, the module user should restrict his use of the module to this public API, resisting the temptation to call any other function or access any other property.

A convention that can help to make the public/private distinction clear, even without reference to the documentation is to prefix private symbols with an underscore. In the discussion of the getCounter( ) accessor function, you can make it clear that the counter property is private by changing its name to _counter. This does not prevent external code from using the property, but it makes it difficult for a programmer to inadvertently use a private property.

Modules distributed through JSAN go a step further. The module definition includes arrays that list the public symbols. The JSAN module named JSAN includes utility functions for importing symbols from a module, and these functions refuse to import symbols that are not explicitly listed as public.

10.2.2. Closures as Private Namespace and Scope

Recall from Section 8.8. that a closure is a function plus the scope that was in effect when the function was defined.[*] By defining a function, therefore, you can use its local scope as a private namespace. Nested functions defined within the outer function have access to this private namespace. The advantages of this are twofold. First, since the private namespace is also the first object on the scope chain, functions in the namespace can refer to other functions and properties in the namespace without requiring a fully qualified name.

[*] Closures are an advanced feature, and if you skipped over their discussion in Chapter 8, you should return to this section after you read about closures.

The second advantage to using a function to define a private namespace has to do with the fact that it is truly private. There is no way to access the symbols defined within the function from outside the function. Those symbols become available only if the function that contains them exports them to an external, public namespace. What this means is that a module can choose to export only its public functions, leaving implementation details such as helper methods and variables locked up in the privacy of the closure.

Example 10-4 helps to illustrate this point. It uses a closure to create a private namespace, and then exports its public methods to a public namespace.

Example 10-4. Defining a private namespace with a closure

 // Create the namespace object.  Error checking omitted here for brevity. var com; if (!com) com = {}; if (!com.davidflanagan) com.davidflanagan = {}; com.davidflanagan.Class = {}; // Don't stick anything into the namespace directly. // Instead we define and invoke an anonymous function to create a closure // that serves as our private namespace. This function will export its // public symbols from the closure into the com.davidflanagan.Class object // Note that we use an unnamed function so we don't create any other // global symbols. (function( ) {  // Begin anonymous function definition     // Nested functions create symbols within the closure     function define(data) { counter++; /* more code here */ }     function provides(o, c) { /* code here */ }     // Local variable are symbols within the closure.     // This one will remain private within the closure     var counter = 0;     // This function can refer to the variable with a simple name     // instead of having to qualify it with a namespace     function getCounter( ) { return counter; }     // Now that we've defined the properties we want in our private     // closure, we can export the public ones to the public namespace     // and leave the private ones hidden here.     var ns = com.davidflanagan.Class;     ns.define = define;     ns.provides = provides;     ns.getCounter = getCounter; })( );          // End anonymous function definition and invoke it 




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net