JS函数申明和闭包的关系实例理解

在网上看到有这样一道关于JavaScript的面试题

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); b.fun(2).fun(3);           //undefined,?,?,?
var c = fun(0).fun(1).fun(2); c.fun(3);           //undefined,?,?,?
var d = fun(0).fun(1).fun(2).fun(3);              //undefined,?,?,?
var e = fun(0);  e.fun(1); e.fun(2).fun(3);       //undefined,?,?,?

//问:四行a,b,c,d,e的输出分别是什么?

这是一道非常典型的JavaScript闭包题。在fun函数数中又嵌套了两层fun函数,搞清楚每层fun的函数是那个fun函数尤为重要。

大家想想这四行的输出值分别是多少?

a行的值:undefined,0,0,0

b行的值:undefined,0,1,2

c行的值:undefined,0,1,1

d行的值:undefined,0,1,2

e行的值:undefined,0,0,2

大家答对了吗?

下面给大大家一一道来,先讲一下三种JavaScript中创建函数的方法:

1、声明函数:最普通最标准的声明函数方法,包括函数名及函数体。

function fn1(){}

2、创建匿名函数表达式:创建一个变量,这个变量的内容为一个函数。

var fn1=function (){}   //注意采用这种方法创建的函数为匿名函数,即没有函数name

3、创建具名函数表达式:创建一个变量,内容为一个带有名称的函数。

var fn1=function name1(){}; //注意:具名函数表达式的函数名只能在创建函数内部使用

因此采用此种方法创建的函数在函数外层只能使用fn1不能使用name1的函数名。name1的命名只能在创建的函数内部使用。

下面用实例验证

var fn1=function name1(){
    console.log("in:fn1<",typeof fn1,"> name1:<",typeof name1,">");
};
console.log("out:fn1<",typeof fn1,"> name1:<",typeof name1,">");
fn1();
//out:fn1< function > name1:< undefined >
//in:fn1< function > name1:< function >

可以看到在函数外部(out)无法使用name1的函数名,为undefined

还有一种情况,在对象内定义函数如var o={ fn1 : function (){…} },也属于函数表达式。

下面用实例验证

//对象内部的函数表达式
var o={
  fn:function (){
    console.log(fn);
  }
};
o.fn();//ERROR报错

错误截图如下:

//非对象内部的函数表达式:
var fn=function (){
  console.log(fn);
};
fn();//function (){console.log(fn);};正确

使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;而在对象内部的不能访问到。原因非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。

综上所述,最内层的return出去的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); b.fun(2).fun(3);           //undefined,?,?,?
var c = fun(0).fun(1).fun(2); c.fun(3);           //undefined,?,?,?
var d = fun(0).fun(1).fun(2).fun(3);              //undefined,?,?,?
var e = fun(0);  e.fun(1); e.fun(2).fun(3);       //undefined,?,?,?

 再看看这个题目,详细说说每行的输出:

var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);

第一个fun(0)是在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数,后面几个fun(2),fun(3)函数都是在调用第二层fun函数。

第一次调用fun(0)时,oundefined

第二次调用fun(1)m1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1n=0,并在内部调用第一层fun函数fun(1,0);所以o0

第三次调用fun(2)m2,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);所以o0;

第四次调用fun(3)m3,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(3,0);所以o0;

最终答案为undefined,0,0,0

var b = fun(0).fun(1); b.fun(2).fun(3); 

第一个fun(0)是在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数,后面几个fun(2),fun(3)函数都是在调用第二层fun函数。

在第一次调用fun(0)时,oundefined

第二次调用fun(1)m1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)m2,此时fun闭包的是第二次调用的n=1,即m=2n=1,并在内部调用第一层fun函数fun(2,1);所以o1

第四次调用fun(3)m3,此时fun闭包的是第三次调用的n=2,即m=3n=2,并在内部调用第一层fun函数fun(3,2);所以o2

最终答案为undefined,0,1,2

var c = fun(0).fun(1).fun(2); c.fun(3); 

第一个fun(0)是在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数,后面几个fun(2),fun(3)函数都是在调用第二层fun函数。

在第一次调用fun(0)时,oundefined

第二次调用fun(1)m1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)m2,此时fun闭包的是第二次调用的n=1,即m=2n=1,并在内部调用第一层fun函数fun(2,1);所以o1

第四次调用.fun(3)m3,此时fun闭包的是第二次调用的n=1,即m=3n=1,并在内部调用第一层fun函数fun(3,1);所以o1

最终答案为undefined,0,1,1

var d = fun(0).fun(1).fun(2).fun(3); 

第一个fun(0)是在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数,后面几个fun(2),fun(3)函数都是在调用第二层fun函数。

在第一次调用fun(0)时,oundefined

第二次调用fun(1)m1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)m2,此时fun闭包的是第二次调用的n=1,即m=2n=1,并在内部调用第一层fun函数fun(2,1);所以o1

第四次调用fun(3)m3,此时fun闭包的是第三次调用的n=2,即m=3n=2,并在内部调用第一层fun函数fun(3,2);所以o2

最终答案为undefined,0,1,2

var e = fun(0);  e.fun(1); e.fun(2).fun(3);

第一个fun(0)是在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数,后面几个fun(2),fun(3)函数都是在调用第二层fun函数。

在第一次调用fun(0)时,oundefined

第二次调用fun(1)m1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;

第三次调用 .fun(2)m2,此时fun闭包的是第一次调用的n=0,即m=2n=0,并在内部调用第一层fun函数fun(2,0);所以o为0

第四次调用fun(3)m3,此时fun闭包的是第三次调用的n=2,即m=3n=2,并在内部调用第一层fun函数fun(3,2);所以o2

最终答案为undefined,0,0,2

到这里大家明白了没有了,如果有不对的地方,请留言告知。

 

猜你喜欢

转载自blog.csdn.net/tomy123456123456/article/details/86478440