Interview questions evolved from work
This is a question I encountered at work. It seemed very interesting, so I made it a question for interviews. I found that almost no one could answer all the questions correctly and tell the reasons, so I took it up and talked about it.
Look at the question code first:
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? //问:三行a,b,c的输出分别是什么?
This is a very typical JS closure problem. There are three levels of fun functions nested in it. It is particularly important to figure out which fun function the fun function of each level is.
You can first write down the results you think on paper or other places, and then expand to see what the correct answer is?
Answer
//a: undefined,0,0,0 //b: undefined,0,1,2 //c: undefined,0,1,1
Did you get everything right? If you answered everything correctly, congratulations. There is almost nothing that can stump you in the js closure problem; if there is no answer, continue to analyze.
There are several functions in JS
First of all, what you need to understand before this is that functions in JS can be divided into two types, named functions (named functions) and anonymous functions.
The method of distinguishing these two functions is very simple. You can judge by outputting fn.name. The one with a name is a named function, and the one without a name is an anonymous function
Note: The name of the named function cannot be obtained on lower versions of IE, and undefined will be returned. It is recommended to test on Firefox or Google Chrome
Or use the IE-compatible get function name method to get the function name:
/** * 获取指定函数的函数名称(用于兼容IE) * @param {Function} fun 任意函数 */ function getFunctionName(fun) { if (fun.name !== undefined) return fun.name; var ret = fun.toString(); ret = ret.substr('function '.length); ret = ret.substr(0, ret.indexOf('(')); return ret; }
Use the above function to test whether it is an anonymous function:
You can know that variable fn1 is a named function and fn2 is an anonymous function
Several ways to create functions
After talking about the types of functions, you also need to understand that there are several ways to create functions in JS.
1. Declare function
The most common and standard way to declare a function, including function name and function body.
function fn1(){}
2. Create anonymous function expression
Create a variable whose content is a function
var fn1=function (){}
Note that the function created using this method is an anonymous function, that is, there is no function name
var fn1=function (){}; getFunctionName(fn1).length;//0
3. Create a named function expression
Create a variable containing a function with a name
var fn1=function xxcanghai(){};
Note: The function name of a named function expression can only be used inside the created function
That is, the function created using this method can only use fn1 and not xxcanghai’s function name in the outer layer of the function. The naming of xxcanghai can only be used inside the created function
Test:
var fn1=function xxcanghai(){ console.log("in:fn1<",typeof fn1,">xxcanghai:<",typeof xxcanghai,">"); }; console.log("out:fn1<",typeof fn1,">xxcanghai:<",typeof xxcanghai,">"); fn1(); //out:fn1< function >xxcanghai:< undefined > //in:fn1< function >xxcanghai:< function >
You can see that the function name of xxcanghai cannot be used outside the function (out), and it is undefined.
Note: Defining a function within an object such as var o={ fn : function (){…} } is also a function expression
4. Function constructor
You can pass a function string to the Function constructor and return a function containing this string command. This method creates an anonymous function.
5. Self-executing function
(function(){alert(1);})(); (function fn1(){alert(1);})();
Self-executing functions belong to the above-mentioned "function expressions" and the rules are the same
6. Other ways to create functions
Of course, there are other ways to create functions or execute functions. I won’t go into details here. For example, using eval, setTimeout, setInterval and other very common methods. I won’t go into too much introduction here. They are non-standard methods. , I won’t expand too much here
What is the relationship between the three fun functions?
After talking about function types and methods of creating functions, you can return to the topic and look at this interview question.
There are three fun functions in this code, so the first step is to figure out the relationship between these three fun functions and which function is the same as which function.
function fun(n,o) { console.log(o) return { fun:function(m){ //... } }; }
先看第一个fun函数,属于标准具名函数声明,是新创建的函数,他的返回值是一个对象字面量表达式,属于一个新的object。
这个新的对象内部包含一个也叫fun的属性,通过上述介绍可得知,属于匿名函数表达式,即fun这个属性中存放的是一个新创建匿名函数表达式。
注意:所有声明的匿名函数都是一个新函数。
所以第一个fun函数与第二个fun函数不相同,均为新创建的函数。
函数作用域链的问题
再说第三个fun函数之前需要先说下,在函数表达式内部能不能访问存放当前函数的变量。
测试1:对象内部的函数表达式:
var o={ fn:function (){ console.log(fn); } }; o.fn();//ERROR报错
测试2:非对象内部的函数表达式:
var fn=function (){ console.log(fn); }; fn();//function (){console.log(fn);};正确
结论:使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的不能访问到。
原因也非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。
所以综上所述,可以得知,最内层的return出去的fun函数不是第二层fun函数,是最外层的fun函数。
所以,三个fun函数的关系也理清楚了,第一个等于第三个,他们都不等于第二个。
到底在调用哪个函数?
再看下原题,现在知道了程序中有两个fun函数(第一个和第三个相同),遂接下来的问题是搞清楚,运行时他执行的是哪个fun函数?
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? //问:三行a,b,c的输出分别是什么?
1. The first line a
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
It can be known that the first fun(0) is calling the first-level fun function. The second fun(1) is the fun function that calls the return value of the previous fun, so:
The last few fun(1), fun(2), fun(3) functions are all calling the second-level fun function.
Then:
When fun(0) is called for the first time, o is undefined;
When fun(1) is called for the second time, m is 1. At this time, fun closes n of the outer function, that is, n=0 for the first call, that is, m=1, n=0, and in The first level fun function fun(1,0) is called internally; so o is 0;
When fun(2) is called for the third time, m is 2, but a.fun is still called, so n from the first call is still closed, so the first layer of fun(2,0) is called internally. ;So o is 0
Same as the fourth time;
That is: the final answer is undefined,0,0,0
2. The second line b
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
Let’s start with fun(0). It must be the first-level fun function called; and its return value is an object, so the second fun(1) calls the second-level fun function. , the following few are also the second-level fun functions called.
Then:
When the first level fun(0) is called for the first time, o is undefined;
When .fun(1) is called for the second time, m is 1. At this time, fun closes n of the outer function, that is, n=0 for the first call, that is, m=1, n=0, and Internally call the first level fun function fun(1,0); so o is 0;
When .fun(2) is called for the third time, m is 2. At this time, the current fun function is not the return object of the first execution, but the return object of the second execution. When the first-level fun function is executed for the second time, (1,0), so n=1, o=0, the second n is closed when returning, so when the third-level fun function is called for the third time, m =2,n=1, that is, the first level fun function fun(2,1) is called, so o is 1;
When .fun(3) is called for the fourth time, m is 3, which closes the n of the third call. Similarly, the final call to the first-level fun function is fun(3,2); so o is 2;
That is the final answer: undefined,0,1,2
3. The third line c
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?
Based on the previous two examples, we can know:
fun(0) executes the first-level fun function, .fun(1) executes the second-level fun function returned by fun(0), the statement ends here, and c stores the return of fun(1) value, rather than the return value of fun(0), so the closure in c is also the value of n when fun(1) is executed for the second time. c.fun(2) executes the second-level fun function returned by fun(1), and c.fun(3) also executes the second-level fun function returned by fun(1).
Then:
When the first level fun(0) is called for the first time, o is undefined;
When .fun(1) is called for the second time, m is 1. At this time, fun closes n of the outer function, that is, n=0 for the first call, that is, m=1, n=0, and Internally call the first level fun function fun(1,0); so o is 0;
When .fun(2) is called for the third time, m is 2. At this time, the fun closure is n=1 for the second call, that is, m=2, n=1, and the first layer of fun is called internally. Function fun(2,1); so o is 1;
The same is true for the fourth time .fun(3), but it is still the return value of the second call, so the first level fun function fun(3,1) is finally called, so o is still 1
That is the final answer: undefined,0,1,1
Later words
This code was originally made when rewriting an asynchronous callback into a synchronous call component. I discovered this pitfall and gained a deeper understanding of JS closures.
There are countless articles on the Internet about what closures are, but to understand what closures are, you still have to discover and understand them yourself in the code.
If you ask me what a closure is, I think closure in a broad sense means that a variable is used in its own scope, which is called closure.
Did everyone answer correctly? I hope readers can gain a better understanding of the closure phenomenon through this article. If you have other insights or opinions, please feel free to correct and discuss.