On recursion and closure in javascript
Keywords:
Javascript
Windows
Attribute
Recursion and closures, as an important part of js, are almost involved in front-end interviews, especially closures. Today, the leader of the front-end group asked me a question and gave a rough answer. I felt dissatisfied, so I relearned and wrote this article.
Before we talk about these two concepts, let's review function expressions.
Function is actually a reference object. Like other reference types, it has properties and methods. Defining functions include function declarations, function expressions, and constructors
Let's talk about the first two.
Function declaration
Function declaration syntax is as follows
function functionName(arg1,arg2,arg3){
//Function body
}
There is an important feature: function declaration promotion
saber();
function saber() {
console.log("excalibar!")//excalibar!
}
It's strange that there is no wrong report! This is because the function declaration is read before the function is executed.
Function expression
The syntax of the function expression is as follows:
Functions in this case are also called anonymous functions.
var functionName = function(arg1,arg2,arg3) {
//Function body
}
However, the function declaration above has to be assigned before using the function expression, otherwise an error will be reported
saber();
var saber = function() {
console.log("excalibar!")//saber is not a function
}
Therefore, before using function expressions, you must var saber ()
recursion
Now, I'm shaking a lot, and I'm going to talk about the theme
Definition: a function calls itself by name
It's very simple. Let's take a look at the code:
//factorial recursive
function saber(num) {
if(num <= 1){
return 1;
}else{
return num*saber(num-1)//saber Can be changed into arguments.callee,This way you can make sure that the function doesn't have any problems
}
}
saber(3)
console.log(saber(3))//6
This is a factorial recursion, but there may be some disadvantages in calling itself by function name. If I define saber as null externally, an error will be reported. Therefore, using arguments.call can ensure that the function will not make an error. However, there are disadvantages. Arguments.call cannot be accessed in strict mode.
So we can use named function expressions to solve this problem
var saber = (function f(num) {
if(num <= 1){
return 1;
}else{
return num * f(num-1)
}
})
saber(3)
console.log(saber(3))//6
This way, even if you assign a function to another variable, f() is still valid and not affected by strict patterns
closure
js, it's disgusting. It's hard to understand
To understand closures, the most important thing is to understand the scope chain, which has a great effect on understanding closures. See my previous blog for scope chain https://www.cnblogs.com/SaberInoryKiss/p/11770974.html
Definition: a function that has access to variables in the scope of another function
Create closure: create another function B inside the A function
function createSavant(propertyName) {
return function(object1,object2) {
var value1 = object1[propertyName];
console.log(value1)//saber
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
}else if(value1 >value2){
return 1;
}else{
return 0;
}
};
}
var savant = createSavant("name")
var result = savant({name:"saber"},{name:"archer"});
savant = null;//Dereference anonymous functions (to free memory)
console.log(result)//0 because the string cannot compare the size, it returns 0. If it is set to a number, for example, var result = savant ({name: "1"}, {name: "2"}), it returns - 1
The above code is a string of closures. We create a return function in the function createsavant. I'll explain it in a simple Vernacular:
First of all, the function created in the function createsavant will contain the scope chain of the external function, that is, the scope chain of return function() will contain the active object of the external createsavant
Therefore, return function() can access all variables defined in the external createSavant. For example, the value of value1 in the above example is obtained by accessing variables defined externally
Then, after the function createsavant is executed, because the scope chain of this guy's return function () still references the active object of the external createsavant, even if the scope chain of the execution environment of createsavant is destroyed, the object of createsavant will still be saved in memory for reference by the internal function return function()
Finally, until the anonymous function ends its evil life and is destroyed. The active object of the external environment createsavant will be destroyed.
What I may say is quite straightforward. Some places are not professional. You can point out the mistakes. I will study modestly.
Here are the advantages and disadvantages of closures:
Disadvantages of closures
(1) occupy too much memory
First, because a closure carries the scope of the function that contains it, it takes up more memory than other normal functions. mmp, for example, for people of the same size, I have a beer belly more than others, so it's not surprising that I'm not heavy. So use closures carefully.
(2) closure can only get the last value containing any variable (important)
This shortcoming can be found in many written interview questions, for example:
function creatFunction() {
var result = new Array();
for(var i = 0; i < 10; i++){//var's variable promotion mechanism leads to the last i only 10 times
result[i] = function() {
return i;
};
}
return result;
}
var saber = creatFunction();
for (var i = 0; i < saber.length; i++) {
console.log(saber[i]())//10 10 10 10 10 10 10 10 10 10
}
The above code looks like each function of the loop should return its own index value, which is 0 1 2 3 4 5 6 7 8 9. But it did return ten tens. The reasons are as follows:
The active object of the createfunction() function is saved in the scope chain of each function. In fact, they all refer to the same variable i. as a result, when createfunction() returns, the value of i is 10, and 10 is saved. Therefore, each function references the variable i with the value of 10, and the result is as shown in the above code.
So how to solve this problem:
Method 1. Create another anonymous function to force the desired effect:
function creatFunction() {
var result = new Array();
for(var i = 0; i < 10; i++){
result[i] = function(num) {
return function() {
return num;
};
}(i);//Many scopes will be generated
}
return result;
}
var saber = creatFunction();
for (var i = 0; i < saber.length; i++) {
console.log(saber[i]())//0 1 2 3 4 5 6 7 8 9
}
As the code added above, instead of assigning closure directly to array, an anonymous function is defined and the result of anonymous function is passed to array. i is passed in when anonymous function is called. Because function is passed by value, the current value of each i in the loop will be copied to parameter num, and then created and returned inside anonymous function (Num) Returned a closure to access num
. Finally, each function in the result array has a copy of the corresponding num, and you can return different values....
It seems that this statement is not easy to understand. To be frank, it is to assign the value of each i to num, and then return the value of all num in the array. It avoids the case that the closure only gets the last value of i.
Method 2. Use let
Because es5 has no block level scope, let is added to es6 to define variables, which makes functions have block level scope
function creatFunction() {
var result = new Array();
for(let i = 0; i < 10; i++){//let There is no variable promotion, and each cycle will be executed once, for Each cycle of is a different block level scope, let Declared variables have block level scope, so there is no duplicate declaration
result[i] = function() {
return i;
};
}
return result;
}
var saber = creatFunction();
for (var i = 0; i < saber.length; i++) {
console.log(saber[i]())//1 2 3 4 5 6 7 8 9
}
(3) closure causes this object to point to windows
var name = "I am windows"
var object = {
name: "saber",
getName : function() {
return function() {
return this.name
}
}
}
console.log(object.getName()())//I am windows
This is bound based on the execution environment of functions, while the execution environment of anonymous functions is global, so this object points to windows
The solution is to save this object in the external scope in a variable that can also be accessed by a closure:
var name = "I am windows"
var object = {
name: "saber",
getName : function() {
var that = this
return function() {
return that.name
}
}
}
console.log(object.getName()())//saber
(4) memory leakage
Due to the existence of anonymous functions, the objects in the external environment will be saved, so the occupied memory will not be recycled by the garbage collection mechanism.
function Savant(){
this.age = "18";
this.name = ["saber","archer"];
}
Savant.prototype.sayName = function(){
var outer = this;
return function(){
return outer.name
};
};
var count = new Savant();
console.log(count.sayName()())//[ 'saber', 'archer' ]
We can save variables to a copy, then reference the copy, and finally set it to empty to free memory
function Savant(){
this.age = "18";
this.name = ["saber","archer"];
}
Savant.prototype.sayName = function(){
var outerName = this.name;
return function(){
return outerName
};
outerName = null;
};
var count = new Savant();
console.log(count.sayName()())//[ 'saber', 'archer' ]
Note: even if this does not solve the problem of memory leakage, we can dereference it to ensure that the memory it occupies is recycled normally.
Having said the disadvantages, let's talk about the advantages of closures
Advantages of closures
(1) mimic block level scope
Syntax:
(function(){
//Here is the block level scope
})();
For example:
function saber(num){
for(var i = 0; i < num; i++){
console.log(i)//0 1 2 3 4
}
// console.log(i)//5
}
saber(5)
You can see that i can still be accessed outside the for loop, so how to install i that cannot be accessed outside the for loop
function saber(num){
(function () {
for(var i = 0; i < num; i++){
// console.log(i)//0 1 2 3 4
}
})();
console.log(i)//i is not defined
}
saber(5)
This method can reduce the memory occupied by closures.
(2) define privilege method in constructor
function savant(name){
var name=name;
this.sayName=function(){
console.log(name);
}
};
var savant1=new savant("saber");
var savant2=new savant("archer");
savant1.sayName(); //saber
savant2.sayName(); //archer
In this example, sayName () is a privileged method, which can be understood as a public method that can be used to access private variables.
(3) static private variable
Privileged methods can also be used in private scopes
(function (){
var name = "";
Savant = function(value) {
name = value;
}
Savant.prototype.getName = function() {
return name;
}
Savant.prototype.setName = function(value) {
name = value;
}
})();
var Savant1 = new Savant("saber")
console.log(Savant1.getName())//saber
Savant1.setName("archer");
console.log(Savant1.getName())//archer
var Savant2 = new Savant("lancer")
console.log(Savant1.getName())//lancer
console.log(Savant2.getName())//lancer
But the difference between the privileged method in the private scope and the constructor is that the privileged method in the private scope is defined on the prototype, so all instances use the same function. As long as a new Savant instance is created or setName() is called, a new value will be given to name in the prototype. The result is that all instances return the same value
(4) module mode
Single instance mode adds private attributes and private methods to reduce the use of global variables
Syntax:
var singleleton = (function(){
// Create private variable
var privateNum = 10;
// Create private function
function privateFunc(){
// Business logic code
}
// Returns an object containing public methods and properties
return {
publicProperty: true,
publicMethod: function() {
//Common method code
}
};
})();
This pattern is very useful when you need to initialize a single instance and maintain its private variables
Enhanced module mode
function Savant() {
this.name = "saber";
};
var application = (function(){
// Defining private ownership
var privateA = "privateA";
// Define private functions
function privateMethodA(){};
// After instantiating an object, return to the instance, and add some public properties and methods to the instance
var object = new Savant();
// Add public attribute
object.publicA = "publicA";
// Add public method
object.publicB = function(){
return privateA;
}
// Return the object
return object;
})();
Savant.prototype.getName = function(){
return this.name;
}
console.log(application.publicA);// publicA
console.log(application.publicB()); // privateA
console.log(application.name); // saber
console.log(application.getName());// saber