JavaScript Advanced programming function expression recursion and closure function _javascript techniques

Source: Internet
Author: User
Tags closure memory usage

There are two ways of defining function expressions: function declarations and function expressions.

The function declaration is as follows:

function functionname (arg0,arg1,arg2) {
 //functions Body
}

First is the function keyword, followed by the name of the functions.

Both Ff,safrai,chrome and opera have defined a nonstandard name attribute that allows access to the name specified by the function. The value of this function is always the same as the identifier following the functions keyword.

Only in Ff,safari,chrome and opera effective
alert (functionname.name)//functionname

A function declaration is characterized by a function declaration elevation (Function declaration hoisting), which means that the functions declaration is read before the code is executed. This means that the function declaration can be placed behind the statement that called it.

Sayhi ();
function Sayhi () {
 alert ("hi!");
}

This example does not throw an error, because the function declaration is read first before the code executes.

The second type is a function expression.

var functionname=function (arg0,arg0,arg2) {
 //function Body
}

This form looks like a regular variable assignment statement, which creates a function and assigns it to the variable functionname. The function created in this case is called an anonymous function (anonymous functions) because there is no identifier after the function keyword. (anonymous functions are sometimes called lambda functions.) The Name property of the anonymous function is an empty string.

function expressions, like other expressions, must be assigned before they are used.

The following code can cause an error:

Syahi ();//uncaught Referenceerror:syahi is not defined
var sayhi=function () {
 alert ("hi!");

Instead of writing code like this, which is an invalid syntax in ECMAScript, the JavaScript engine tries to fix the error, but different browsers change it.

Do not do this
if (condition) {
 function sayhi () {
  alert ("hi!");
 }
else{
 function Sayhi () {
  alert ("yo!");
 }

If you're using a function expression, there's no problem.

Can do this
var sayhi;
if (condition) {
 sayhi=function () {
  alert ("hi!");
 }
} else{
 sayhi=function () {
  alert ("yo!");
 }

The ability to create a function to assign a value to a variable can also return a function as a value of another function.

function Creatcomparisonfunction (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;
  }
 ;
}

Creatcomparisonfunction () returns an anonymous function. The returned function may be assigned to a variable or otherwise invoked; However, within the creatcomparisonfunction () function, It's anonymous. You can use an anonymous function when you use a function as a value.

7.1 Recursion

A recursive function is a function that is constructed by invoking its own name.

function factorial (num) {
 if (num<=1) {return
  1;
 } else{return
  num*factorial (num-1);
 }

Above is a classical recursive factorial function. The following code may cause it to fail.

var anotherfactorial=factorial;
Factorial=null;
Alert (Anotherfactorial (4));//uncaught typeerror:factorial is not a function

The above code first saves the factorial () function in the variable anotherfactorial, and then sets the factorial variable to null, and the result points to the original reference with only one left. The next call to Anotherfactorial (), Because factorial () must be executed, and factorial () is no longer a function, it can cause an error.

In this case, the use of Arguments.callee can be resolved.

Arguments.callee is a pointer to a function that is executing, so you can use it to implement a recursive call to a function.

function factorial (num) {
 if (num<=1) {return
  1;
 } else{return
  Num*arguments.callee (num-1);
 }

When writing recursive functions, using Arguments.callee is always safer than using a function name, because it ensures that no matter how the function is invoked.

However, in strict mode, Arguments.callee cannot be accessed through scripting.

However, you can use a function expression to achieve the same result.

var factorial= (function f (num) {
 if (num<=1) {return
  1;
 } else{return
  num*f (num-1);
 }
);
Console.log (factorial (4));//24

7.2 Closures

A closure is a function that has access to a variable in another function scope. The common way to create closures is to create another function inside one function.

function Creatcomparisonfunction (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;
  }
 ;
}

The two lines of code that are bold are the code in the internal function (an anonymous function) that accesses the variable propertyname in the external function. Even if this intrinsic function is returned and is called elsewhere, But it still has access to variable propertyname. This variable is also accessible because the scope of the internal function's scoped chain contains the scope of Creatcomparisonfunction ().

When a function is invoked, an execution environment (execution context) and the corresponding scope chain are created. The active object (activation object) of the function is then initialized with the values of arguments and other named parameters. But in the scope chain, The active object of an external function is always in the second position, and the outer function's active object is in the third position,.... Up to the global execution environment as the endpoint of the scope chain.

During function execution, to read and write the value of a variable, you need to find the variable in the GU scope chain.

function Compare (value1,value2) {
  if (value1<value2) {
   return-1;
  } else if (value1>value2) {return
   1;
  } else{return
   0;
  }
 var result=compare (5,10)
 console.log (Result)//-1

The above code first defines the compare () function and then calls it in the global scope. When compare () is invoked, a live object containing arguments,value1,value2 is created. Variable objects for the global execution Environment ( Contains result and compare) is second in the scope chain of the Compare () execution Environment

Each execution environment in the background has an object that represents the variable--the Variable object. The variable objects of the global environment always exist, and the variable objects of a local environment such as the Compare () function exist only during the execution of the function. When you create the Compare () function, Creates a scope chain that contains the global variable object beforehand, and the scope chain is saved in the internal [[scope]] property. When the Compare () function is invoked, an execution environment is created for the function, and then by copying the function's [[scope]] property to build the scope chain of the execution environment. After that, an active object (used here as a variable object) is created and pushed into the front-end of the execution environment scope chain.

A scope chain is essentially a list of pointers to variable objects that reference but do not actually contain variable objects.

Whenever you access a variable in a function, a variable with the corresponding name is searched from the scope chain. Generally, when the function completes, the local active object is destroyed and only the global scope (the variable object of the global execution environment) is saved in memory. However, closures are different.

A function defined inside another function adds the active object containing the function (that is, the external function) to its scope.

var compare=creatcomparisonfunction ("name");
var result=comapre ({name: "Nicholas"},{name: "Greg"});

The following figure shows the scope of the function and the internal anonymous function when the code above is executed.


When the Createcomparisonfunction () function returns, the scope of its execution environment is destroyed, but its active object remains in memory, and the active object of Createcomparisonfunction () is destroyed until the anonymous function is destroyed .

Create function
 var compare=creatcomparisonfunction ("name");
 Call function
 var result=comapre ({name: "Nicholas"},{name: "Greg"});
 Unbind a reference to an anonymous function (in order to free memory)
 Comparenames=null;

By setting comparenames equal to null to dismiss the reference to the function, it is tantamount to notifying the garbage collection routine of clearing it. As the scope chain of anonymous function functions is destroyed, other scopes (in addition to global scope) can be destroyed safely.

Because the closure carries the scope of the function that contains it, it takes up more memory than the other functions. Excessive use of closures can result in excessive memory usage and prudent use of closures.

7.2.1 Closures and variables

The mechanism of this configuration of the scope chain leads to a noticeable side effect, that is, the closure can only take the last value of any variable in the function.

The closure holds the entire variable object, not a particular variable.

function Createfunctions () {
 var result=new Array ();

 for (Var i=0;i<10;i++) {
  result[i]=function () {return
   i;}
  ;
 }
 return result;
}

In the code above, this function returns an array of functions. On the surface, it seems that each function should return its own index value, but in fact, each function returns 10. Because the active object of the Createfunctions () function is stored in the scope chain of each function, So they all refer to the same variable i. When the Createfunction () function returns, the value of the variable i is 10, at which point each function references the same variable object that holds the variable i, so the value of I within each function is 10.

However, we can force the closure behavior to conform to expectations by creating another anonymous function.

function Createfunctions () {
 var result=new Array ();

 for (Var i=0;i<10;i++) {
  result[i]=function (num) {return
   function () {return
    num;
   }
  } (i);
 }
 return result;
}

Once overridden, each function returns its own different index values. In this release, instead of assigning the closure directly to the array, we define an anonymous function and assign the result of the immediate execution of the anonymous function to the array. The anonymous function here has a parameter num, The value to be returned by the final function. When we call each anonymous function, we pass in the variable i. Because the function parameter is passed by value, the current value of the variable i is copied to the parameter num. Inside this anonymous function, a closure that accesses NUM is created and returned. Each function in the result array has its own copy of the NUM variable, so it is possible to return different values.

7.2.2 about this object

The This object is bound to the execution environment of the basic function at run time: in a global function, this equals window, and this equals that object when the function is called as a method of an object. However, the execution environment of an anonymous function is global, so its this object usually points to window .

var name= "the window";
var object={
 Name: ' My object ',

 getnamefunc:function () {return
  function () {return
   this.name;
 }}
};
Alert (Object.getnamefunc () ());//the window (in non-strict mode)

Each function automatically obtains two special variables when invoked: this and arguments. Intrinsic functions Search These two variables only until they are active, so it is never possible to directly access the two variables in the external function.

However, by keeping the This object in the external scope in a variable that a closure can access, you can allow the closure to access the object.

var name= "the window";
var object={
 Name: "My Object",

 getnamefunc:function () {
  var that=this;
  return function () {return
   that.name;
};}}; Alert (Object.getnamefunc () ());//my Object

Before you define an anonymous function, we assign the this object to a variable named that. After the closure is defined, the closure can also access the variable because it is a variable that we deliberately declare in the containing function. Even after the function returns, that still references object. So the call to Object.getnamefunc () () returns my object.

Note: This and arguments also have the same problem. If you want to access the arguments object in the scope, you must save a reference to that object to a variable that the other closure can access.

var name= "the window";

var object={
 Name: ' My object ',

 getname:function () {return
  this.name;
 }
};
Console.log (Object.getname ());//my Object
console.log ((object.getname) ());//my Object
Console.log ( Object.getname=object.getname) ());//the window

The last line of code executes an assignment statement and then invokes the result of the assignment. Because the value of this assignment expression is the function itself, the value of this cannot be maintained, and the result returns the "this window".

7.2.3 Memory leaks

Because the previous version of IE9 uses a different garbage collection routine for JScript objects and COM objects, so closures can cause special problems in these versions of IE. specifically, if an HTML element is stored in the scope chain of the closure, it means that the element will not be destroyed.

function Assignhandlet () {
 var Element=document.getelementbyid ("Someelement");
 Element.onclick=function () {
  alert (element.id);
 };
}

The above code creates a closure that acts as an event handler for element elements, and the closure creates a circular reference. Because the anonymous function holds a reference to the active object of Assignhandler (), Therefore, the number of references that cause the element cannot be reduced. As long as the anonymous function exists, the number of references to the element is at least 1, so the memory it occupies will never be reclaimed. But this problem can be solved by slightly rewriting the code.

function Assignhandlet () {
 var Element=document.getelementbyid ("Someelement");
 var id=element.id;

 Element.onclick=function () {
  alert (ID);
 };
 Element=null;
}

In the code above, by keeping a copy of element.id in a variable, and referencing the variable in the closure, the circular reference is eliminated.

Cloud Habitat Community Friendship reminds you: closures refer to the entire active object that contains the function, which contains the element. Even if the closure does not refer to the element directly, the active object containing the function still holds a reference. Therefore, It is necessary to set the element variable to NULL. This allows a reference to a DOM object to be lifted, a smooth reduction in its reference count, and ensuring that the memory it occupies is properly recycled.

Here is a description of the following function expression.

In JavaScript programming, function expressions are a very useful technique. You can use a function expression to implement dynamic programming without naming the function. Anonymous functions, also known as lambda functions, are a powerful way to use JavaScript functions. The following summarizes the features of function expressions.

A function expression differs from a function declaration. A function declaration requires a name, but a function expression is not required. A function expression without a name is also called an anonymous function.

Recursive functions become more complex when you cannot determine how to reference a function, and recursive functions should always use Arguments.callee to recursively invoke itself, rather than using the function name--the name of the functions may change.

When other functions are defined inside a function, a closure is created. Closures have access to all variables inside the function, and the principle
As follows.

In a background execution environment, the scope chain of a closure contains its own scope, the scope of the containing function, and the global scope. Typically, the scope of a function and all its variables are destroyed after the function has finished executing.

However, when a function returns a closure, the scope of the function will remain in memory until the closure does not exist.

Using closures, you can mimic block-level scopes in JavaScript (JavaScript itself does not have a block-level scope concept), as follows.

Create and call a function immediately so that you can execute the code in it without leaving a reference to the function in memory.
The result is that all the variables inside the function are destroyed immediately--unless some of the variables are assigned to a variable in the scope (that is, an external scope).
Closures can also be used to create private variables in objects, with concepts and points as follows. Even though there is no formal concept of a private object attribute in JavaScript, the closure can be implemented as a public method, and the variables defined in the containing scope can be accessed through public methods.
Public methods that have access to private variables are called privileged methods.

You can use a constructor pattern, a prototype pattern, to implement a privileged method of a custom type, or you can use a modular pattern, an enhanced module pattern, to implement a single privileged method.

Functional expressions and closures in JavaScript are extremely useful features that can be used to achieve many functions. However, because creating closures must maintain additional scopes, excessive use of them may consume a large amount of memory.

Related Article

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.