Friday, June 29, 2007

Start study JavaScript

   Ща не ща, налага се да уча JavaScript. Както винаги почнах да го разглеждам отдалече. Като за начало ми трябваше нещо като въведение. Потъсих в google и се спрях на ре-интро. Не е кой знае какво, но важните концепции са там. Разбира се това съвсем не е достатъчно се огледах за следващата порция. Намерих я в вид на видео лекции. Те са на Douglas Crockford, който се води JS гурото на Yahoo. Много добри практики показва този човек. Не ги разбрах всичките и затова ще трябва пак да ги прегледам. Ето и линковете:

Beginers
http://video.yahoo.com/video/play?vid=111593&fr=
http://video.yahoo.com/video/play?vid=111594&fr=
http://video.yahoo.com/video/play?vid=111595&fr=
http://video.yahoo.com/video/play?vid=111596&fr=

Advanced
http://video.yahoo.com/video/play?vid=111585&fr=
http://video.yahoo.com/video/play?vid=111586&fr=
http://video.yahoo.com/video/play?vid=111587&fr=
and
http://video.yahoo.com/watch/630959/2974197

Super Advanced
http://ejohn.org/apps/learn/ 
http://jibbering.com/faq/faq_notes/closures.html
 

Newest
https://www.youtube.com/watch?v=ya4UHuXNygM&playnext=1&list=PL7664379246A246CB&feature=results_main

DOM
http://video.yahoo.com/video/play?vid=111582&fr=
http://video.yahoo.com/video/play?vid=111583&fr=
http://video.yahoo.com/video/play?vid=111584&fr= 
 
Others
http://yuiblog.com/crockford/ 

So far so good. Имам някакви базови знания, ама как да започна да пиша и да бриша. Тъй като много от нещата си пиша в Eclipse и Emacs трябваше да ги настроя. За Eclipse свалих JSEclipse, a тук е update сайта. При Emacs има избор, най-доброто мисля е това. В него има инструкция как да се инсталира. Редакторите ги докарах, ама как ще тествам нещата. Оказа се, че има JavaScript Shell. Много добре работи. Ето как го настроих.
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot login
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co -l mozilla/js/src mozilla/js/src/config mozilla/js/src/editline mozilla/js/src/fdlibm

cd mozilla/js/src
make -f Makefile.ref

И сега нещата са много лесни. Правя си update преди да почна:
cd mozilla
cvs up

cd mozilla/js/src
make -f Makefile.ref

Направих си и alias в .bashrc. Така че и откъдето и да напиша js си отварям js-shell.
alias js='/home/zlatozar/js-shell/mozilla/js/src/Linux_All_DBG.OBJ/js -i'

Удобно е да провериш нещо набъзо. Особенно за примерите от книгата или ако има някаква по заплетена ситуация. Май имам всичко необходимо. Сега само четене и практика. И разбира се много debug с чудесния Firefox plug-in FireBug. Поставянето на breakpoint става като се напише debugger в кода. Нищо работа.

Друг вариант е да се ползва Rhino.
Като чета нещо в случая книгата "JavaScript Definitive Guide" си подчертавам най-важните, така че лесно да мога да се върна към тях и да ги запомня. Ето и линк към примерите от книгата http://www.davidflanagan.com/javascript5/

И най-важното е да се разбере, че JavaScript e:
A language which has Scheme like semantics and Java like syntax

Core JavaScript Language Notes:





Intro
When a JavaScript interpreter is embedded in a web browser, the result is client-side JavaScript.
Mozilla provides two different versions of the JavaScript 1.5 interpreter. One is written in C and is called SpiderMonkey. The other is written in Java and, in a flattering reference to this book, is called Rhino. Useful tool that is not strictly a debugger is jslint, which looks for common problems in JavaScript code (see jslint). JavaScript programs are written using the Unicode character set. It is a case-sensitive language. Note, however, that HTML is not case-sensitive (although XHTML is).

Lexical Structure
You may omit the semicolon if each of your statements is placed on a separate line. JavaScript allows you to work with three primitive datatypes: numbers, strings of text (known as strings), and Boolean truth values (known as booleans). It also defines two trivial datatypes, null and undefined, each of which defines only a single value. In addition to these primitive datatypes, JavaScript supports a composite datatype known as an object. An object (that is, a member of the datatype object) represents a collection of values (either primitive values, such as numbers and strings, or composite values, such as other objects). Objects in JavaScript have a dual nature: an object can represent an unordered collection of named values or an ordered collection of numbered values. JavaScript defines another special kind of object, known as a function. A function is an object that has executable code associated with it. A function may be invoked to perform some kind of operation.

Numbers
JavaScript differs from programming languages such as C and Java in that it does not make a distinction between integer values and floating-point values. All numbers in JavaScript are represented as floating-point values.

Strings
A string is a sequence of Unicode letters, digits, punctuation characters, and so on; it is the JavaScript datatype for representing text. A string comprises a sequence of zero or more Unicode characters enclosed within single or double quotes (' or "). Double-quote characters may be contained within strings delimited by single-quote characters, and single-quote characters may be contained within strings delimited by double quotes. One of the built-in features of JavaScript is the ability to concatenate strings. If you use the + operator with numbers, it adds them. But if you use this operator on strings, it joins them by appending the second to the first. Numbers are automatically converted to strings when needed. If a number is used in a string concatenation expression. To make number-to-string conversions more explicit, use the String( ) function:
var string_value = String(number); 
Another technique for converting numbers to strings uses the toString( ) method:
string_value = number.toString( );  
When a string is used in a numeric context, it is automatically converted to a number. This means, for example, that the following code actually works:
var product = "21" * "2"; // product is the number 42
To allow more flexible conversions, you can use parseInt( ) and parseFloat( ). These functions convert and return any number at the beginning of a string, ignoring any trailing nonnumbers. parseInt( ) parses only integers, while parseFloat( ) parses both integers and floating-point numbers. If a string begins with 0x or 0X, parseInt( ) interprets it as a hexadecimal number.
Strings are intentionally immutable. Strings are compared by value.

Booleans
Boolean values are represented by the literals true and false. Boolean values are easily convertible to and from other types and are often automatically converted. If a boolean value is used in a numeric context, true converts to the number 1 and false converts to the number 0. If a boolean value is used in a string context, true converts to the string "true" and false converts to the string "false". If a number is used where a boolean value is expected, the number is converted to True unless the number is 0 or NaN, which are converted to false. If a string is used where a boolean value is expected, it is converted to true except for the empty string, which is converted to false. null and the undefined value convert to false, and any non-null object, array, or function converts to true.

Objects
An object is a collection of named values. These named values are usually referred to as properties of the object. (Sometimes they are called fields of the object, but this usage can be confusing.) To refer to a property of an object, you refer to the object, followed by a period and the name of the property. When a function value is stored in a property of an object, that function is often called a method, and the property name becomes the method name. To invoke a method of an object, use the . syntax to extract the function value from the object and then use the ( ) syntax to invoke that function. For example, to invoke the write( ) method of an object named document, you can use code like this:
document.write("this is a test"); 
Objects in JavaScript can serve as associative arrays; that is, they can associate arbitrary data values with arbitrary strings. When an object is used in this way, a different syntax is generally required to access the object's properties: a string containing the name of the desired property is enclosed within square brackets. Using this syntax, you can access the properties of the image object mentioned previously with code like this:
image["width"] 
image["height"] 
An object literal (also called an object initializer) consists of a comma-separated list of colon-separated property/value pairs, all enclosed within curly braces. Thus, the object point in the previous code can also be created and initialized with this line:
var point = { x:2.3, y:-1.2 }; 
When a non-null object is used in a Boolean context, it converts to true. When an object is used in a string context, JavaScript calls the toString( ) method of the object and uses the string value returned by that method. When an object is used in a numeric context, JavaScript first calls the valueOf( ) method of the object.

Douglas Crockford: "JavaScript is fundamentally about objects. Arrays are objects. Functions are objects. Objects are objects. So what are objects? Objects are collections of name-value pairs. The names are strings, and the values are strings, numbers, booleans, and objects (including arrays and functions). Objects are usually implemented as hashtables so values can be retrieved quickly.

If a value is a function, we can consider it a method. When a method of an object is invoked, the this variable is set to the object. The method can then access the instance variables through the this variable.

Objects can be produced by constructors, which are functions which initialize objects. Constructors provide the features that classes provide in other languages, including static variables and methods."

Arrays
An array is a collection of data values, just as an object is. While each data value contained in an object has a name, each data value in an array has a number, or index. In JavaScript, you retrieve a value from an array by enclosing an index in square brackets after the array name. Arrays may contain any type of JavaScript data, including references to other arrays or to objects or functions. For example:
document.images[1].width 
Associative arrays are indexed by strings. Also note that JavaScript does not support multidimensional arrays, except as arrays of arrays. Finally, because JavaScript is an untyped language, the elements of an array do not all need to be of the same type, as they do in typed languages like Java. Creating:
var a = new Array( );
a[3] = { x:1, y:3 }; 

var a = new Array(10); 
var b = [1.2, "JavaScript", true, { x:1, y:3 }];
Undefined elements can be included in an array literal by simply omitting a value between commas. For example, the following array contains five elements, including three undefined elements:
var sparseArray = [1,,,,5]; 
You can access elements of an array using square brackets ([]), and you can access elements of an object using a dot (.). Both [] and . are treated as operators in JavaScript.
The . operator expects an object as its left-side operand and an identifier (a property name) as its right-side operand. The right-side operand should not be a string or a variable that contains a string; it should be the literal name of the property or method, without quotes of any kind. Here are some examples:
document.lastModified 
navigator.appName 
frames[0].length 
document.write("hello world") 
If the specified property does not exist in the object, JavaScript does not issue an error but instead simply returns undefined as the value of the expression.

null and undefined
The JavaScript keyword null is a special value that indicates no value. null is usually considered a special value of object typea value that represents no object. null is a unique value, distinct from all other values. When a variable holds the value null, you know that it does not contain a valid object, array, number, string, or boolean value. When null is used in a Boolean context, it converts to false. When used in a numeric context, it converts to 0. And when used in a string context, it converts to "null".
undefined is returned when you use either a variable that has been declared but never had a value assigned to it or an object property that does not exist. Note that this special undefined value is not the same as null.

Wrapper Objects
JavaScript besides supporting the number, string, and boolean datatypes, JavaScript also supports Number, String, and Boolean classes. These classes are wrappers around the primitive datatypes. A wrapper contains the same primitive data value, but it also defines properties and methods that can be used to manipulate that data. Note that any number, string, or boolean value can be converted to its corresponding wrapper object with the Object( ) function:
var number_wrapper = Object(3); 

By Reference, by Value
The basic rule in JavaScript is this: primitive types are manipulated by value, and reference types, as the name suggests, are manipulated by reference. Numbers and booleans are primitive types in JavaScript primitive because they consist of nothing more than a small, fixed number of bytes that are easily manipulated at the low levels of the JavaScript interpreter. Objects, on the other hand, are reference types. Arrays and functions, which are specialized types of objects, are therefore also reference types. These datatypes can contain arbitrary numbers of properties or elements, so they cannot be manipulated as easily as fixed-size primitive values can.

Varables
An important difference between JavaScript and languages such as Java and C is that JavaScript is untyped. This means, in part, that a JavaScript variable can hold a value of any datatype, unlike a Java or C variable, which can hold only the one particular type of data for which it is declared. A feature related to JavaScript's lack of typing is that the language conveniently and automatically converts values from one type to another, as necessary. Variables declared with var are permanent: attempting to delete them with the delete operator causes an error.

If you attempt to read the value of an undeclared variable, JavaScript generates an error. If you assign a value to a variable that you have not declared with var, JavaScript implicitly declares that variable for you. Note, however, that implicitly declared variables are always created as global variables, even if they are used within the body of a function.
To prevent the creation of a global variable (or the use of an existing global variable) when you meant to create a local variable to use within a single function, you must always use the var statement within function bodies. It's best to use var for all variables, whether global or local.


Within the body of a function, a local variable takes precedence over a global variable with the same name. If you declare a local variable or function parameter with the same name as a global variable, you effectively hide the global variable. JavaScript does not have block-level scope. All variables declared in a function, no matter where they are declared, are defined throughout the function. In the following code, the variables i, j, and k all have the same scope: all three are defined throughout the body of the function.
function test(o) {     
   var i = 0;                        // i is defined throughout function 
   if (typeof o == "object") { 
      var j = 0;                     // j is defined everywhere, not just block 
      for(var k=0; k < 10; k++) {  // k is defined everywhere, not just loop 
         document.write(k); 
      } 
      document.write(k);            // k is still defined: prints 10 
   } 
   document.write(j);               // j is defined, but may not be initialized 
} 
Local variable is defined throughout, it is not actually initialized until the var statement is executed.
REMEMBER:
function f( ) {     
   var scope;        // Local variable is declared at the start of the function 
   alert(scope);     // It exists here, but still has "undefined" value 
   scope = "local";  // Now we initialize it and give it a value 
   alert(scope);     // And here it has a value 
} 
It is good programming practice to place all your variable declarations together at the start of any function.

Undeclared variables are undefined because they simply do not exist. As described earlier, assigning a value to an undeclared variable does not cause an error; instead, it implicitly declares the variable in the global scope. The second kind of undefined variable is one that has been declared but has never had a value assigned to it. If you read the value of one of these variables, you obtain its default value, undefined. Variables in JavaScript are fundamentally the same as object properties.

When the JavaScript interpreter starts up, one of the first things it does, before executing any JavaScript code, is create a global object. The properties of this object are the global variables of JavaScript programs. When you declare a global JavaScript variable, what you are actually doing is defining a property of the global object. In top-level code (i.e., JavaScript code that is not part of a function), you can use the JavaScript keyword this to refer to the global object. Within functions, this has a different use.

Each time the JavaScript interpreter begins to execute a function, it creates a new execution context for that function. An execution context is, obviously, the context in which any piece of JavaScript code executes. An important part of the context is the object in which variables are defined. Thus, JavaScript code that is not part of any function runs in an execution context that uses the global object for variable definitions. And every JavaScript function runs in its own unique execution context with its own call object in which local variables are defined. Client-side JavaScript code in each frame or window runs in its own execution context and has its own global object.

Every JavaScript execution context has a scope chain associated with it. This scope chain is a list or chain of objects. When JavaScript code needs to look up the value of a variable x (a process called variable name resolution), it starts by looking at the first object in the chain. If that object has a property named x, the value of that property is used. If the first object does not have a property named x, JavaScript continues the search with the next object in the chain. If the second object does not have a property named x, the search moves on to the next object, and so on.

Expressions and Operators
When constructing JavaScript expressions, you must pay attention to the datatypes that are being passed to operators and to the datatypes that are returned. Different operators expect their operands' expressions to evaluate to values of a certain datatype. For example, it is not possible to multiply strings, so the expression "a" * "b" is not legal in JavaScript. Note, however, that JavaScript tries to convert expressions to the appropriate type whenever possible, so the expression "3" * "5" is legal. Its value is the number 15, not the string "15". The == and === operators check whether two values are the same, using two different definitions of sameness. Both operators accept operands of any type, and both return true if their operands are the same and false if they are different. The === operator is known as the identity operator, and it checks whether its two operands are "identical" using a strict definition of sameness. The == operator is known as the equality operator; it checks whether its two operands are "equal" using a more relaxed definition of sameness that allows type conversions.
// If max_width is defined, use that.  Otherwise look for a value in 
// the preferences object.  If that is not defined use a hard-coded constant. 
var max = max_width || preferences.max_width || 500;

Statements
with(object)
  statement
This statement effectively adds object to the front of the scope chain, executes statement, and then restores the scope chain to its original state.
// Access form elements directly here. For example: 
with(frames[1].document.forms[0]) {     
   name.value = ""; 
   address.value = ""; 
   email.value = ""; 
} 
Objects and Arrays
An important point to notice about object properties is that you can create a new property of an object simply by assigning a value to it. Although you declare variables with the var keyword, there is no need (and no way) to do so with object properties. Furthermore, once you have created an object property by assigning a value to it, you can change the value of the property at any time simply by assigning a new value:
book.title = "JavaScript: The Rhino Book" 
Objects are often called an associative array - a data structure that allows you to dynamically associate arbitrary values with arbitrary strings. The term map is often used to describe this situation as well: JavaScript objects map strings (property names) to values. In JavaScript, every object has a constructor property that refers to the constructor function that initializes the object. The instanceof operator checks the value of the constructor property, so the code above could also be written:
if ((typeof o == "object") && (o instanceof Date))  // Then do something with the Date object... 
Function
Functions may have parameters, or arguments local variables whose value is specified when the function is invoked. Functions often use these arguments to compute a return value that becomes the value of the function-invocation expression. When a function is invoked on an object, the function is called a method, and the object on which it is invoked is passed as an implicit argument of the function. If the "return" statement does not have an associated expression, it returns the "undefined" value.
If you pass more arguments than the function expects, the function ignores the extra argumetnts. If you pass fewer than expected, the parameters you omit are given the undefined value.

Example of nested function:
function hypotenuse(a, b) { 
   function square(x) { return x*x; } 
   return Math.sqrt(square(a) + square(b)); 
}
Nested functions may be defined only at the top level of the function within which they are nested. That is, they may not be defined within statement blocks, such as the body of an if statement or while loop. Note that this restriction applies only to functions defined with the function statement. Function literal expressions, may appear anywhere. Example of function literal:
var f = function fact(x) { if (x <= 1) return 1; else return x*fact(x-1); }; 
Because function literals are created by JavaScript expressions rather than statements, they are quite flexible and are particularly well suited for functions that are used only once and need not be named. For example, the function specified by a function literal expression can be stored into a variable, passed to another function, or even invoked directly:
f[0] = function(x) { return x*x; };                // Define a function and store it
a.sort(function(a,b){return a-b;});                // Define a function; pass it to another
var tensquared = (function(x) {return x*x;})(10);  // Define and invoke 
Function names are often verbs or phrases that begin with verbs. It is a common convention to begin function names with a lowercase letter. When a name includes multiple words, one convention is to separate words with underscores like_this(); another convention is to begin all words after the first with an uppercase letter likeThis(). Functions that are supposed to be internal or hidden are sometimes given names that begin with an underscore.

Optional Arguments
When a function is invoked with fewer arguments than are declared, the additional arguments have the undefined value! It is often useful to write functions so that some arguments are optional and may be omitted when the function is invoked. Note that when designing functions with optional arguments, you should be sure to put the optional ones at the end of the argument list so that they can be omitted. The programmer who calls your function cannot omit the first argument and pass the second, for example. In this case, he would have to explicitly pass undefined or null as the first argument. Functions like this one that can accept any number of arguments are called variadic functions, variable arity functions, or varargs functions. Within the body of a function, the identifier arguments has special meaning. arguments is a special property that refers to an object known as the Arguments object. The Arguments object is an array-like object that allows the argument values passed to the function to be retrieved by number, rather than by name. In addition to its array elements, the Arguments object defines a callee property that refers to the function that is currently being executed. This property is rarely useful, but it can be used to allow unnamed functions to invoke themselves recursively. For instance, here is an unnamed function literal that computes factorials:
function(x) { 
   if (x <= 1) return 1; 
   return x * arguments.callee(x-1); 
} 
Functions as Data
In JavaScript, however, functions are not only syntax but also data, which means that they can be assigned to variables, stored in the properties of objects or the elements of arrays, passed as arguments to functions, and so on.
var a = square(4);  // a contains the number 16 var b = square;     
// Now b refers to the same function that square does 
var c = b(5);       // c contains the number 25 
Functions as Methods
A method is nothing more than a JavaScript function that is stored in a property of an object and invoked through that object. Methods have one very important property: the object through which a method is invoked becomes the value of the this keyword within the body of the method. Thus, when you invoke o.m(), the body of the method can refer to the object o with the this keyword. Here is a concrete example:
var calculator = {       
   // An object literal     operand1: 1, 
   operand2: 1, 
   compute: function() { 
            this.result = this.operand1 + this.operand2; 
   } 
}; 
calculator.compute();       // What is 1+1? 
print(calculator.result);   // Display the result 
IMPORTANT: When a function is invoked as a function rather that as a method, the this keyword refers to the global object.
Confusingly, this is true even when a nested function is invoked (as a function) within a containing method that was invoked as a method: the this keyword has one value in the containing function but (counterintuitive) refers to the global object within the body of the nested function. The length property of a function itself, however, has a different meaning. This read-only property returns the number of arguments that the function expects to be passed that is, the number of parameters it declares in its parameter list.

ECMAScript specifies two methods that are defined for all functions, call() and apply(). These methods allow you to invoke a function as if it were a method of some other object. The first argument to both call() and apply() is the object on which the function is to be invoked; this argument becomes the value of the this keyword within the body of the function.

Lexical Scoping
Functions in JavaScript are lexically rather than dynamically scoped. This means that they run in the scope in which they are defined, not the scope from which they are executed. When a function is defined, the current scope chain is saved and becomes part of the internal state of the function. At the top level, the scope chain simply consists of the global object, and lexical scoping is not particularly relevant. When you define a nested function, however, the scope chain includes the containing function. This means that nested functions can access all of the arguments and local variables of the containing function.

The Call Object
When the JavaScript interpreter invokes a function, it first sets the scope to the scope chain that was in effect when the function was defined. Next, it adds a new object, known as the call object (the ECMAScript specification uses the term activation object) to the front of the scope chain. The call object is initialized with a property named arguments that refers to the Arguments object for the function. Named parameters of the function are added to the call object next. Any local variables declared with the var statement are also defined within this object. Since this call object is at the head of the scope chain, local variables, function parameters, and the Arguments object are all in scope within the function. This also means that they hide any properties with the same name that are further up the scope chain, of course. Note that unlike arguments, this is a keyword, not a property in the call object.

Classes, Constructors, and Prototypes
You have also seen other kinds of JavaScript objects created with a similar syntax:
var array = new Array(10); 
var today = new Date( ); 
The new operator must be followed by a function invocation. It creates a new object, with no properties and then invokes the function, passing the new object as the value of the this keyword. A function designed to be used with the new operator is called a constructor function or simply a constructor. A constructor's job is to initialize a newly created object, setting any properties that need to be set before the object is used.
// Define the constructor. Note how it initializes the object referred to by "this". 
function Rectangle(w, h) { 
   this.width = w; 
   this.height = h; 
   // Note: no return statement here 
} 
Notice how the constructor uses its arguments to initialize properties of the object referred to by the this keyword. You have defined a class of objects simply by defining an appropriate constructor function; all objects created with the Rectangle( ) constructor are now guaranteed to have initialized width and height properties. This means that you can write programs that rely on this fact and treat all Rectangle objects uniformly. It turns out that every JavaScript object includes an internal reference to another object, known as its prototype object. Any properties of the prototype appear to be properties of an object for which it is the prototype. Another way of saying this is that a JavaScript object inherits properties from its prototype.
After creating the empty object, new sets the prototype of that object. The prototype of an object is the value of the prototype property of its constructor function. All functions have a prototype property that is automatically created and initialized when the function is defined. The initial value of the prototype property is an object with a single property. This is clearer with an example. Here is the Rectangle( ) constructor:
// The constructor function initializes those properties that 
// will be different for each instance. 
function Rectangle(w, h) { 
   this.width = w; 
   this.height = h; 
} 
// The prototype object holds methods and other properties that 
// should be shared by each instance. 
Rectangle.prototype.area = function( ) { return this.width * this.height; } 
This means that the prototype object is an ideal place for methods and other constant properties. When you read property p of an object o, JavaScript first checks to see if o has a property named p. If it does not, it next checks to see if the prototype object of o has a property named p. This is what makes prototype-based inheritance work. When you write the value of a property, on the other hand, JavaScript does not use the prototype object. Therefore, property inheritance occurs only when you read property values, not when you write them.Note that you must never add properties to Object.prototype.

Using closures
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. 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.
// 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 

Module Pattern

Global variables are evil. Within YUI, we use only two globals: YAHOO and YAHOO_config. Everthing in YUI makes use of members within the YAHOO object hierarchy or variables that are scoped to such a member. We advise that you exercise similar discipline in your own applications, too. Douglas Crockford has been teaching a useful singleton pattern for achieving this discipline, and I thought his pattern might be of interest to those of you building on top of YUI. Douglas calls this the “module pattern.” Here’s how it works:

1. Create a namespace object: If you’re using YUI, you can use the YAHOO.namespace() method:
YAHOO.namespace("myProject");
This assigns an empty object myProject as a member of YAHOO (but doesn’t overwrite myProject if it already exists). Now we can begin adding members to YAHOO.myProject.

2. Assign the return value of an anonymous function to your namespace object:
YAHOO.myProject.myModule = function () {
   return  {
            myPublicProperty: "I'm accessible as YAHOO.myProject.myModule.myPublicProperty.";
            myPublicMethod: function () {
            YAHOO.log("I'm accessible as YAHOO.myProject.myModule.myPublicMethod.");
   }
};

}(); // the parens here cause the anonymous function to execute and return
Note the very last line with the closing curly brace and then the parentheses () — this notation causes the anonymous function to execute immediately, returning the object containing myPublicProperty and myPublicMethod. As soon as the anonymous function returns, that returned object is addressable as YAHOO.myProject.myModule.

3. Add “private” methods and variables in the anonymous function prior to the return statement. So far, the above code hasn’t bought us any more than we could have gotten by assigning myPublicProperty and myPublicMethod directly to YAHOO.myProject.myModule. But the pattern does provide added utility when we place code before the return statement:
YAHOO.myProject.myModule = function () {
                              //"private" variables:
                              var myPrivateVar = "I can be accessed only from within YAHOO.myProject.myModule.";

                              //"private" method:
                              var myPrivateMethod = function () {
                              YAHOO.log("I can be accessed only from within YAHOO.myProject.myModule");
                            }

return  {
   myPublicProperty: "I'm accessible as YAHOO.myProject.myModule.myPublicProperty."
   myPublicMethod: function () {
                      YAHOO.log("I'm accessible as YAHOO.myProject.myModule.myPublicMethod.");

                     // Within myProject, I can access "private" vars and methods:
                     YAHOO.log(myPrivateVar);
                     YAHOO.log(myPrivateMethod());

                     // The native scope of myPublicMethod is myProject; we can
                     // access public members using "this":
                     YAHOO.log(this.myPublicProperty);
                   }
  };
}(); // the parens here cause the anonymous function to execute and return

In the codeblock above, we’re returning from an anonymous function an object with two members. These members are addressable from within YAHOO.myProject.myModule as this.myPublicProperty and this.myPublicMethod respectively. From outside of YAHOO.myProject.myModule, these public members are addressable as YAHOO.myProject.myModule.myPublicProperty and YAHOO.myProject.myModule.myPublicMethod. The private variables myPrivateProperty and myPrivateMethod can only be accessed from within the anonymous function itself or from within a member of the returned object. They are preserved, despite the immediate execution and termination of the anonymous function, through the power of closure — the principle by which variables local to a function are retained after the function has returned. As long as YAHOO.myProject.myModule needs them, our two private variables will not be destroyed.

4. Do something useful with the pattern. Let’s look at a common use case for the module pattern. Suppose you have a list, some of whose list items should be draggable. The draggable items have the CSS class draggable applied to them.
<!--This script file includes all of the YUI utilities:-->
<script type="text/javascript"
src="http://yui.yahooapis.com/2.2.2/build/utilities/utilities.js"></script>

<ul id="myList">
<li class="draggable">Item one.</li>
<li>Item two.</li> <!--item two won't be draggable-->
<li class="draggable">Item three.</li>
</ul>

<script>
YAHOO.namespace("myProject");
YAHOO.myProject.myModule = function () {

//private shorthand references to YUI utilities:
var yue = YAHOO.util.Event,
yud = YAHOO.util.Dom;

//private method:
var getListItems = function () {

                   //note that we can use other private variables here, including
                   //our "yud" shorthand to YAHOO.util.Dom:
                   var elList = yud.get("myList");
                   var aListItems = yud.getElementsByClassName(
                      "draggable",     //get only items with css class "draggable"
                      "li",            //only return list items
                       elList          //restrict search to children of this element
                       );
                  return aListItems;
};

//the returned object here will become YAHOO.myProject.myModule:
return  {

          aDragObjects: [],    // a publicly accessible place to store our DD objects
          init: function () {
                // we'll defer making list items draggable until the DOM is fully loaded:
                yue.onDOMReady(this.makeLIsDraggable, this, true);
          },

          makeLIsDraggable: function () {
                var aListItems = getListItems(); // these are the elements we'll make draggable
                for (var i=0, j=aListItems.length; i<j; i++) {
                   this.aDragObjects.push(new YAHOO.util.DD(aListItems[i]));
                }
}

};
}(); // the parens here cause the anonymous function to execute and return

// The above code has already executed, so we can access the init
// method immediately:
YAHOO.myProject.myModule.init();
</script>

This example is a simple one, and it’s deliberately verbose — if this was all we were doing, we could doubtless write it in a more compact way. However, this pattern scales well as the project becomes more complex and as its API grows. It stays out of the global namespace, provides publicly addressable API methods, and supports protected or “private” data and methods along the way.

Function scope can create an encapsulation. Use an anonymous function to wrap your application.




ПП Понякога не е достатъчно и за бърза справка ползвам това.

1 comment:

Anonymous said...

Този линк може да е полезен на хората: http://www.smashingmagazine.com/2009/02/08/50-extremely-useful-javascript-tools/

algorithms (1) cpp (3) cv (1) daily (4) emacs (2) freebsd (4) java (3) javascript (1) JSON (1) linux (2) Lisp (7) misc (8) programming (16) Python (4) SICP (1) source control (4) sql (1) думи (8)