Home  >  Article  >  Web Front-end  >  JavaScript Advanced Programming (3rd Edition) Study Notes 9 js Function (Part 2)_Basic Knowledge

JavaScript Advanced Programming (3rd Edition) Study Notes 9 js Function (Part 2)_Basic Knowledge

WBOY
WBOYOriginal
2016-05-16 17:49:23915browse

Then let's look at functions - magical objects.

9. Function as value

In general programming languages, if you want to use a function as a value, you need to use a function pointer or proxy. Implementation, but in ECMAScript, a function is an object that has all the characteristics of a general object. In addition to a function having its own properties and methods, it can also be used as a reference type value. In fact, our previous example We have already used functions as the value of an object attribute. For example, a function can also be used as a parameter or return value of another function. The callback function in asynchronous processing is a typical usage.

Copy code The code is as follows:

var name = 'linjisong';
var person = {name:'oulinhai'};
function getName(){
return this.name;
}
function sum(){
var total = 0,
l = arguments.length;
for(; l; l--)
{
total = arguments[l-1];
}
return total;
}

// Define the function that calls the function, using the function as the formal parameter
function callFn(fn,arguments,scope){
arguments = arguments || [];
scope = scope || window;
return fn.apply(scope, arguments);
}
// Call callFn, using the function as the actual parameter
console.info(callFn(getName));//linjisong
console.info(callFn(getName,'',person));//oulinhai
console.info(callFn(sum,[1,2,3,4]));//10

Look at another typical example of using a function as a return value. This example comes from Chapter 5 of the original book:
Copy code The code is as follows:

function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[ propertyName];

if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}

var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29 }];

data.sort(createComparisonFunction("name"));
console.info(data[0].name); //Nicholas

data.sort(createComparisonFunction ("age"));
console.info(data[0].name); //Zachary


10. Closure (Closure)

A closure is a function that has access to a variable in the scope of another function. Objects are data with functions, and closures are functions with data.

First, a closure is a function, and then a closure is a function with data. So, what data does it carry? Let's look at the example of function as return value. What is returned is an anonymous function. As this anonymous function is returned, the outer createComparisonFunction() function code is executed. According to the previous conclusion, the outer function The execution environment will be popped off the stack and destroyed, but in the subsequent sorting, you can see that the propertyName in the scope of createComparisonFunction() can still be accessed in the returned anonymous function, which shows that although the execution environment corresponding to createComparisonFunction() has been destroyed , but the active object corresponding to this execution environment has not been destroyed, but is used as an object in the scope chain of the returned anonymous function. In other words, the data contained in the closure composed of the returned anonymous function is: The corresponding activity object of the outer function. Since the properties of the active object (that is, the variables, functions and formal parameters defined in the outer function) will change as the code of the outer function is executed, the data contained in the closure composed of the anonymous function that is finally returned is the outer layer The active object after the function code execution is completed, which is the final state.

I hope you can understand the above paragraph carefully and understand it again and again. Although I have tried my best to make it easier to understand, the concept of closure is still a bit abstract. Let's take a look at an example. This example comes from Chapter 7 of the original book:
Copy code The code is as follows:

function createFunctions(){
var result = new Array();
for (var i=0 ; i < 10; i ){
result[i] = function(){
return i;
};
}
return result;
}

var funcs = createFunctions();
for (var i=0,l=funcs.length; i < l; i ){
console.info(funcs[i]());// Each function outputs 10
}

Here, since the data contained in the closure is the final state of the active object corresponding to createFunctions, and after the createFunctions() code is executed, the attribute i of the active object has become 10, so each of the following calls returns All functions output 10. To deal with this problem, you can use anonymous function scope to save the state:
Copy code Code As follows:

function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i ){
result[ i] = (function(num){
return function(){
return num;
};
})(i);
}
return result;
}

Save each state using an anonymous function that is called immediately (saved in the active object corresponding to the anonymous function), and then when the final returned function is called, it can be closed through The data contained in the package (the data in the corresponding anonymous function activity object) is accessed correctly, and the output result becomes 0,1,...9. Of course, by doing this, 10 closures will be created, which will have a greater impact on performance. Therefore, it is recommended not to abuse closures. In addition, because closures will save active objects of other execution environments as part of their own scope chain loop, which may also cause memory leaks. Although closures have hidden dangers in efficiency and memory, the function of closures is too powerful. Let's take a look at the applications of closures - first, let's go back to the function binding method bind() mentioned yesterday.

(1) Function binding and currying

A. Look at this again, first an example (Chapter 22 of the original book):
Copy code The code is as follows:


If you click the "Hello" button, what will be printed on the console? It turned out to be a Button, not the expected Event. The reason is that when the button is clicked, the internal property this of the handler function points to the button object. You can use closures to solve this problem:
Copy code The code is as follows:

btn.onclick = function(event){
handler.handleClick(event);//Form a closure, the object handler is the one who calls the function, the internal attribute this of the function points to the handler object, so Event will be output}

B. The above solution is not elegant. There is a new function binding method bind() in ES5. We use this method to rewrite it:
Copy the code The code is as follows:

if(!Function.prototype.bind){//bind is new in ES5. In order to ensure normal operation, it is not supported when Add this method on your browser
Function.prototype.bind = function(scope){
var that = this;//The function object that calls the bind() method
return function(){
that.apply(scope, arguments);//Use the apply method to specify the internal property of the that function object this
};
};
}
btn.onclick = handler.handleClick.bind( handler);//When using the bind() method, you only need to use one statement

In the bind() method added here, the main technology is to create a closure to save the parameters during binding As the internal attribute this when the function is actually called. If you are not sure whether the browser itself supports bind() or our bind() works here, you can remove the conditional judgment of feature detection and try changing the method name.
C. When using the bind() method for the function above, only the first parameter is used. If multiple parameters are passed in when calling bind() and the second parameter is used as the parameter when the function is actually called, Then we can bind default parameters to the function.
Copy code The code is as follows:

if(!Function.prototype.bind){
Function.prototype.bind = function(scope){
var that = this;//The function object that calls the bind() method
var args = Array.prototype.slice.call(arguments,1);//Array of parameters starting from the 2nd parameter
return function(){
var innerArgs = Array.prototype.slice.apply (arguments);
that.apply(scope, args.concat(innerArgs));//Use the apply method, specify the internal property this of the that function object, and fill in the parameters passed in when binding
};
};
}

D. Currying: When binding above, the first parameter is used to set the internal attribute this when the function is called. If all The parameters during binding are all used as pre-filled parameters, which is called function currying.
Copy code The code is as follows:

if(!Function.prototype.curry){
Function.prototype.curry = function(){
var that = this;//The function object that calls the curry() method
var args = Array.prototype.slice.call(arguments);//Pre-filled Parameter array
return function(){
var innerArgs = Array.prototype.slice.apply(arguments);//Parameter array when actually called
that.apply(this, args.concat(innerArgs)) ;//Use the apply method and add pre-filled parameters
};
};
}

(2) Use closure cache

Also Remember the function that used recursion to implement the Fibonacci sequence? Use the closure cache to rewrite it:
Copy the code The code is as follows:

var fibonacci = ( function(){//Use closure cache, recursion
var cache = [];
function f(n){
if(1 == n || 2 == n){
return 1;
}else{
cache[n] = cache[n] || (f(n-1) f(n-2));
return cache[n];
}
}
return f;
})();

var f2 = function(n){//Do not use closure cache, recurse directly
if(1 = = n || 2 == n){
return 1;
}else{
return f2(n-1) f2(n-2);
}
};

The following is the test code and the running results on my machine:
Copy the code The code is as follows:

var test = function(n){
var start = new Date().getTime();
console.info(fibonacci(n));
console.info( new Date().getTime() - start);

start = new Date().getTime();
console.info(f2(n));
console.info(new Date().getTime() - start);
};
test(10);//55,2,55,2
test(20);//6765,1,6765,7
test(30);//832040,2,832040,643

It can be seen that the larger the n value, the more obvious the advantage of using cache calculation. As an exercise, you can try modifying the function that calculates the factorial yourself.

(3) Imitate block-level scope

In ECMAScript, there are statement blocks, but there is no corresponding block-level scope, but we can use closures to imitate block-level scope. , the general format is:
Copy code The code is as follows:

(function(){
//Here is the block statement
})();

The above pattern is also called an immediately called function expression. This pattern has become very popular, especially due to jQuery Source code has become popular on a large scale using this method.
Closures also have many interesting applications, such as imitating private variables and private functions, module patterns, etc. We will not discuss them here for now. We will look at these contents after deeply understanding the object.

Regarding functions, let’s talk about these first. There are many excellent articles on the Internet. If you are interested, you can search and read them yourself. Here is an article recommended, a translation by the translator of "JavaScript Advanced Programming (3rd Edition)": Exploring Named Function Expressions.
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn