JavaScript (4-2)

Source: Internet
Author: User
4.9In programming languages, scopes control the visibility and lifecycle of variables and parameters. This is an important help for programmers because it reduces name conflicts and provides automatic memory management. Most languages that use the C language syntax have block-level scopes. All variables defined in a code block (including the statement set in a pair of curly braces) are invisible outside the code block. Variables defined in the Code block are released after the code block is executed. This is a good thing. Worse, although the code block syntax seems to support block-level scopes, JavaScript does not. This obfuscation may be the source of errors. JavaScript does have function scopes. Parameters and variables defined in a function are invisible outside the function. But variables defined at any position in a function are visible anywhere in the function (speaking silently: My God, it's a disaster ....). Many modern languages recommend declaring variables as late as possible. However, using JavaScript becomes a bad suggestion because it lacks block-level scope. Therefore, it is best to declare all variables that may be used in the function at the top of the function body. 4.10Closure only has function scopes. The advantage is that internal functions can access parameters and variables (except this and arguments) that define their external functions ). This is a very good thing. Our getelementsbyattribute function can work because it declares a results variable and the internal function passed to performance_the_dom can also access the results variable. A more interesting situation is that internal functions have a longer life cycle than their external functions. Previously, we constructed a myobject object with a value attribute and an increment method. Suppose we want to protect this value from illegal changes. Unlike the previous definition of an object, myobject is initialized by calling a function, which returns an object. This function defines a value variable. This variable is always visible to the increment and getvalue methods, but the function scope makes it invisible to other programs. VaR myobject = function (){ VaR value = 0; Return { Increment: function (INC ){ Value + = typeof Inc === 'number '? INC: 1; }, Getvalue: function (){ Return value; } } }();We didn't assign a function to myobject. We assigned the result returned after calling this function (silently speaking: Pay attention to the last line ()). This function returns an object that contains two methods, and these methods continue to enjoy the privilege to access the value variable. The quo constructor before this chapter generates an object with the status attribute and the get_status method. But it does not seem very interesting. Why use a getter method to access the attributes that can be directly accessed? If status is a private property, it makes more sense. So Let's define another form of quo function to do this: // Create a constructor named quo. // It constructs an object with the get_status method and status private attribute. VaR quo = function (Status ){ Return { Get_status: function (){ Return status; }, Set_status: function (ST ){ Status = sT; } }; }; // Construct a quo instance VaR myquo = quo ("amazed "); Document. writeln (myquo. get_status ());This quo function is designed to be used without adding new to the front, so there is no upper letter in the name (silent talk: Of course, you can also add new, the effect is the same ). When we call quo, it returns a new object containing the get_status method. A reference of this object is stored in myquo. The get_status method still has the permission to access status even if the quo function has finished running. The get_status method does not access a copy of this parameter. It accesses the parameter itself. This function can access the context when it is created. This is called a closure. // Define a function. It sets a DOM node to yellow and changes it to white. VaR fade = function (node ){ VaR level = 1; VaR step = function (){ VaR hex = level. tostring (16 ); Node. style. backgroundcolor = '# ffff' + hex; If (level <15 ){ LEVEL + = 1; SetTimeout (step, 100 ); } }; Step (); }; <Body onload = "fade (document. Body)"> </body>We call fade and pass document. body as a parameter to it (the node created by the HTML <body> tag). The Fade function sets the level to 1. It defines a step function, and then calls the step function. The Fade function ends. The step function converts the level variable of the fade function into hexadecimal characters. Then, it modifies the background color of the node obtained by the fade function. Then, view the level variable of the fade function. If the background is not white, increase the level variable and use setTimeout to run it again. The step is quickly called again, and the fade function is already running, but the variable of the fade function will be retained as long as the internal function of the fade function is needed! Great Closure !!!). It is very important to understand the actual variables that internal functions can access external functions, rather than a copy. refer to the following example. // Bad example // Construct a function and set an event handler for the nodes in an array in an incorrect way. // When you click a node, a dialog box should pop up as expected to display the node serial number. // However, all events always display the number of nodes. VaR add_the_handlers = function (nodes ){ VaR I; For (I = 0; I <nodes. length; I ++ ){ Nodes [I]. onclick = function (e ){ Alert (I); // because the variable I is referenced directly here, rather than the copy, the I value after the loop is always displayed when you click a node. } } } <Body onload = "add_the_handlers (document. getelementsbytagname ('div ')"> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> </Body>The add_the_handlers function aims to provide a unique value for each event processing function (speak silently: that is, the I value for each loop. It requires many copies of I, and each I value is different ), however, it directly references I, so each event processing function gets the final value of I after the loop. // Good example // Construct a function and set an event handler for the nodes in an array in the correct way. // When you click a node, different serial numbers will pop up. VaR add_the_handlers = function (nodes ){ VaR I; For (I = 0; I <nodes. length; I ++ ){ Nodes [I]. onclick = function (e ){ Return function (){ Alert (E ); }; } (I ); } };  <Body onload = "add_the_handlers (document. getelementsbytagname ('div ')"> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> <Div style = "width: 300px; Height: 300px; Border: 1px solid black;"> </div> </Body> </Html>Now, we define a function and pass I immediately to execute it, instead of assigning a function to onclick. The function returns an event handler. This event processing function prints e instead of I, so that we can avoid the above situation (speak silently: the Chinese version of the source code is incorrect, and the Chinese translation is also wrong, it took me half an hour to understand the intention of the text. In order to make it easy for readers to understand, I modified the function) 4.11The callback function makes processing of discontinuous events easier. For example, assume that there is a sequence that starts with user interaction and sends a request to the server to display the server response. The simplest method may be like this: Request = prepare_the_request (); Response = send_request_synchronously (request ); Display (response );The problem with this method is that the synchronization on the network will cause the client to enter the suspended state. If the network transmission or server is slow, the reduction in responsiveness will be unacceptable. A better way is to initiate an asynchronous request and provide a callback function that will be called when the server's response arrives. In this way, the client will not be blocked. Request = prepare_the_request (); Send_request_asynchronously (request, function (response ){ Display (response ); })(Speak silently: do not try to run these two pieces of code, because these two pieces of code are just for illustration and are pseudo-code) 4.12A module is a function or object that provides interfaces but hides states and implementations. We can use functions and closures to construct a module. By using functions to generate modules, We can almost discard the use of global variables, so as to alleviate the impact of one of the worst features of JavaScript. For example, suppose we want to add a deentityify Method to the string. Its task is to find the HTML character entities in the string and replace them with their corresponding characters. It makes sense to save the names of character entities in an object and their corresponding characters. But where can we save this object? We can put it in a global variable, but the global variable is the devil. We can define it in this function, but there is a loss of runtime, because the definition will be initialized every time the function is executed. The ideal way is to put it into a closure, String. Method ('entityify ', function (){ // Character ing table, which maps character names to corresponding characters VaR entity = { Quot :'"', LT: '<', GT: '>' }; // Return the deentityify Method Return function (){ // This is the deentityify method. It calls the string replace method, // Search for substrings starting with '&' and ending. If these characters can be found in the character ing table, // Replace the character with the value in the ing table. It uses a regular expression (see chapter 7) Return this. Replace (/& ([^ &;] +);/g, Function (a, B ){ VaR r = entity [B]; Return typeof R === 'string '? R:; } ); }; }());Pay attention to the last line. We use the () method to immediately call the function we just constructed. The deentityify method is used to call the function created and returned. Document. writeln ("& lt; & quot; & gt;". deentityify (); // output <">The module mode uses function scopes and closures to create associations between bound objects and private members. In this example, only the deentityify method has the permission to access the data object of the character ing table. The general form of module mode is: a function that defines private variables and functions, and uses closures to create privileged functions that can access private variables and functions. Finally, this privileged function is returned, or save them to an accessible place. With the module mode, you can discard the use of global variables. It promotes information hiding and other excellent design practices. For application encapsulation or construction of other Singleton objects, the singleton of the annotation JavaScript creates an object by defining the object. It is usually used as a tool to provide Function Support for other parts of the program .), The module mode is very effective. The module mode can also be used to generate secure objects. Suppose we want to construct an object for generating serial numbers: VaR serialmaker = function (){ // Returns an object used to generate a unique string. // A unique string consists of two parts: prefix and serial number. // This object contains a method to set the prefix and a method to set the serial number. // And a gensym method that generates a unique string VaR prefix = ''; VaR seq = 0; Return { Setprefix: function (p ){ Prefix = string (P ); }, Setseq: function (s ){ SEQ = s; }, Gensym: function (){ VaR result = prefix + seq; SEQ + = 1; Return result; } }; };  VaR seqer = serialmaker (); Seqer. setprefix ('q '); Seqer. setseq (1000 ); VaR unique = seqer. gensym (); // The value of unique is "q1000" Alert (unique );Neither this nor that is used in seqer. Therefore, there is no way to damage seqer. The value of prefix or seq cannot be changed unless the corresponding method is called. The seqer object is variable, so its method may be replaced, but the replaced method still cannot access private members. Seqer is a set of functions, and those functions are granted permissions to use or modify private states. 4.13Cascade has some methods that do not return values. If we want these methods to return this instead of undefined, we can start cascade. In a cascade statement, we can call multiple methods of the same object in sequence in a separate statement. An Ajax class library that enables cascading may allow us to encode in this form: // Speak silently: This code is only used to describe the concept of cascade and cannot be run. In fact, cascade is the form of continuous hitting call methods in Java. Getelement ('myboxdiv '). Move (350,150 ). Width (100 ). The height (100 ). Color ('red '). Border ('10px outset '). Padding ('4px '). Appendtext ('Please stand '). On ('mousedown', function (m ){ This. startdrag (M, this. getninth (m )); }). On ('mousemove ', 'drag '). On ('mouseup', 'stopdrag '). Later (2000, function (){ This. color ('yellow '). Sethtml ("What hath God wraught? "). Slide (200,200 ); }). Tip ('this box is resizeable ');In this example, the getelement function generates a DOM element corresponding to id = "myboxdiv" and provides other functions. This method allows us to move an element, modify its size and style, and add behavior. Each of these methods returns this object, so the results returned by the call can be used by the next call. Cascade can generate an interface with strong expressiveness. It can also help control the trend of constructing interfaces that try to do a lot of things at a time (silently speaking: To be honest, I really don't like this encoding because it is too easy to parse. The cascaded library applies to the code that is no longer modified after one encoding, or to the code that you do not want to read, including yourself ). 4.14Using a function is also a value, so that we can operate the function value in an interesting way. Apply allows us to combine the function with the parameter passed to it to generate a new function. VaR Add1 = Add. Curry (1 ); Document. writeln (Add1 (6); // The result written in the book is 7, but my actual debugging result is undefined.Add1 is a function created after passing 1 to the curry method of the add function. The Add1 function adds 1 to its parameters. JavaScript does not have the curry method, but we may add a function to function. prototype: Function. Method ('curry', function (){ VaR slice = array. Prototype. Slice, ARGs = slice. Apply (arguments ), That = this; Return function (){ Return that. Apply (null, argS. Concat (slice. Apply (arguments ))); }; });The curry method creates a closure that includes the original function and the parameters used by the quilt. The curry method returns another function. When the function is called, a result is returned. The result includes the parameters passed in by the curry method and their own parameters. It uses the concet method of array to connect them together. Because the arguments array is not a real array, it does not have the Concat method. To avoid this problem, we must apply the Slice Method of the array on both arguments arrays. This generates a regular array with the Concat method. 4.15The memoization function can use objects to remember the results of previous operations, thus avoiding unnecessary operations. This kind of optimization is called the memcached method (memoization: an optimization technique used to speed up the program operation. The Chinese version of the original book is translated as memory, and I translate it as a memcached method here ). It is very convenient to optimize JavaScript objects and arrays. For example, we want a recursive function to calculate Fibonacci. A Fibonacci number is the sum of the first two. The first two digits are 0 and 1. VaR maid = function (n ){ Return n <2? N: Maid (n-1) + maid (n-2 ); }  For (VAR I = 0; I <= 10; I ++ ){ Document. writeln ('//' + I + ':' + maid (I) + '<br/> '); } Running result: // 0: 0 // 1:1 // 2:1 // 3: 2 // 4: 3 // 5: 5 // 6: 8 // 7: 13 // 8: 21 // 9: 34 // 10: 55The program can work, but the Fibonacci function is called 453 times. We called it 11 times, and it called it 442 times. If we let this function apply the moji method, we can significantly reduce its calculation workload. We store our stored results in an array named memo, and the stored results can be hidden in the closure. When our function is called, the function first checks whether the storage result is known. If the storage result is known, the storage result is returned immediately. VaR maid = function (){ VaR memo = [0, 1]; VaR fib = function (n ){ VaR result = memo [N]; If (typeof result! = 'Number '){ Result = fib (n-1) + fib (n-2 ); Memo [N] = result; } Return result; }; Return fib; }();This function returns the same result, but it is only called 29 times. We called it 11 times. It calls it 18 times. We can generalize this form and write a function to help us construct a function with the moji function. The memoizer function gets an initial memo array and fundamental function. It returns a shell function that manages memo storage and calls the fundamental function as needed. We pass the shell function and the parameter of the function to the fundamental function: VaR memoizer = function (memo, fundamental ){ VaR shell = function (n ){ VaR result = memo [N]; If (typeof result! = 'Number '){ Result = fundamental (shell, N ); Memo [N] = result; } Return result; }; Return shell; };Now, we can use memoizer to define the Fibonacci function and provide its initial memo array and fundamental function: VaR Fibonacci = memoizer ([], function (shell, n ){ Return shell (n-1) + shell (n-2 ); });By designing functions that can generate other functions, we can greatly reduce the necessary work. For example, to generate a factorial function of the moji method, we only need to provide the basic factorial formula: VaR factorial = memoizer ([1, 1], function (shell, n ){ Return N * shell (n-1 ); });

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.