一道闭包经典题目

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的输出分别是什么?

我理解的闭包:

  闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

  上面的题目就相当于内部return的fun读取全局声明的fun作用域内的变量。

我们需要了解的知识点:

1、JS中有几种函数类型

  具名函数(命名函数)

  匿名函数

2、创建函数的几种方式

  ① 声明函数

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

function fn1(){}

  ② 创建匿名函数表达式

    创建一个变量,变量的内容为一个函数。此种方式创建的函数为匿名函数。

var fn1=function (){}

  ③ 创建具名函数表达式

    创建一个变量,变量的内容为一个具名函数。此种方式创建的函数,只能在创建的函数内部使用test调用,在函数外部只能使用fn1来调用此函数。

var fn1=function test(){};

  ④ Function构造函数

    可以给Function构造函数传入一个函数字符串,返回包含这个函数字符串的匿名函数。  

    

   ⑤ 自执行函数

    

 

 

 3、三个fun函数的关系如何?

  分析这道题,题目中出现了三个fun,首先搞清楚三个fun之间的关系。

  第一个fun函数,是标准具名函数声明,是新创建的函数,它的返回值是一个对象字面表达式,属于一个新的object。

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      //...
    }
  };
}

  所有匿名生成的函数都是一个新函数。所以第一个fun和第二个fun不相同,均为新创建的函数。

4、函数作用域链

  声明函数时各函数都有各自的作用域,相互关联的函数形成一条作用域链,函数在执行时,从上到下(从里到外)查找使用到的变量。

  在说第三个fun之前需要说下函数作用域链,在函数表达式内部能不能访问存放当前函数的变量。

  ① 访问对象对象内部的函数表达式

  

 

 

  ②  访问非对象内部的函数表达式

  

 

 

   采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。

  所以最内层return出去的函数,是第一个在全局var声明的fun。

 

5、题目分析

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的输出分别是什么?

  第一行a是在调用第一个fun, 后三个a.fun均是在调用第二个fun。

    第一次调用fun时,传入fun(0),n = 0, o = undefined。

    第二次调用a.fun(1)时,调用的是return出的函数, m = 1 ,此时闭包取到上层函数的n值,n = 0。所以a.fun(1) 输出 0 。

    第三次调用a.fun(2)时,相当于重新调用第一个fun内部return出的fun函数,m = 2,闭包取到上层函数的n,n = 0。a.fun(2)依旧是 0。

    第四次同上,a.fun(3) 输出 0。

    结果为 undefined, 0, 0, 0

  第二行b是一条链式调用,第一个fun(0)调用的是第一个fun函数,fun(0).fun(1)调用的是第二个fun函数(根据函数作用域链,先从自身找fun,没有的话再向上查找),fun(0).fun(1).fun(2),fun(0).fun(1).fun(2).fun(3)调用都是第二个fun函数。

    fun(0)传入n = 0 , o = undefined。

    fun(0).fun(1),n = 0 , m = 1 此时调用到外层时,n = 1, o = 0,输出 o 的值 为0。

    fun(0).fun(1).fun(2),因为闭包在第一个函数内,所以可以接着取到上次的n值为1,再次调用第二个fun时,m = 2, n = 1。相当于第一个fun的 n = 2, o = 1,所以输出 1 。  

    fun(0).fun(1).fun(2).fun(3),同第三步, m = 3, n = 2,相当于 n = 3, o = 2。输出 o的值 2。

    结果为undefined, 0, 1, 2 

  理解了第二行的调用,第三行c的调用就很容易了

    fun(0).fun(1),调用第一个fun传入n = 0, o = undefiend,再调用内部return的fun传入1,0, 输出 o值 0 。

    c.fun(2), 调用第二个fun函数,传入2,1,输出 1。

    c.fun(3), 同上,输出 1。

    结果为 undefined, 0, 1, 1

 

posted @ 2021-09-06 14:18  进阶之路-前端  阅读(451)  评论(0编辑  收藏  举报