资讯专栏INFORMATION COLUMN

【前端芝士树】Js中的闭包是怎么一回事 && 笔试问题集锦

awokezhou / 829人阅读

摘要:前端芝士树中的闭包是怎么一回事笔试问题集锦为什么会有闭包的出现这涉及到作为变量声明的关键词时所出现的一些问题。另一方面,在函数外部自然无法读取函数内的局部变量。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

【前端芝士树】Js中的闭包是怎么一回事 && 笔试问题集锦 为什么会有闭包的出现?

这涉及到var作为变量声明的关键词时所出现的一些问题。
比如,var 的 变量提升 以及 函数级作用域

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
另一方面,在函数外部自然无法读取函数内的局部变量。

需要注意的是,如果在函数内部声明变量时没有使用var关键词,实际上声明的是一个全局变量,如下:

function f1(){
    n = 999;
}
//console.log(n); // ReferenceError: n is not defined
f1();
console.log(n); // 999

这里为什么第一处会报错呢?关于这个问题下面有比较基础的解释:

JS的解析过程分为两个阶段:预编译期(预处理)执行期

预编译期 JS会对本代码块(两个script块互不影响)中的所有var声明的变量和函数进行处理(类似与C语言的编译)
此时处理函数的只是声明式函数,而且变量也只是进行了声明但未进行初始化以及赋值。>

执行期 会按照代码块的顺序筑行执行

正因为从外部访问在函数内部进行声明的局部变量是不可能的,所以出现了闭包这种形式,在函数内部再定义一个函数。

闭包(Closure)是什么?

查阅了一些文章和资料,发现还是下面的定义最容易理解:

闭包:定义在函数内部的一个函数。

扩展一些讲,可以参考一下阮一峰的讲解:

闭包:能够读取其他函数内部变量的函数。

俗话说的好,看定义不如看代码更直观一些,如下

function f1(){
    var n=999;
    function f2(){
        console.log(n);
    }
    return f2;
}

var result=f1();
result(); // 999

如此段代码所示,f2()就是其中的闭包函数,通过f2()我们可以访问到f1()内部的n
更常见的一种简写形式:

function f1(){
    var n=999;
    return function(){
        console.log(n);
    };
}

var result=f1();
result(); // 999
闭包的优缺点

优点:

可以读取函数内部的变量

让这些变量的值始终保持在内存中。

缺点:

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包面试题集锦(持续更新) 问题一、简单闭包
var a = 1;
function foo(){
    var a = 2;
    c = 0;
    return function () {
        console.log(a);
        console.log(b++);
        console.log(c);
    }
}
console.log(a);// 1

//console.log(c); // Reference Error
var b = 3;
var x = foo();
x(); //2 3 0
console.log(a); // 1
console.log(b); // 4
console.log(c); // 0
问题二、闭包的链式调用
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);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);
看解答前先思考一下会输出什么哦

【解答】: 首先对函数进行分析,这个函数其实是返回了一个对象,{fun:function(){}},里面有一个函数作为属性,这个函数就是闭包,使得函数内部的变量保留在内存中。

注意,这里会有一个可能误解的地方,return {fun:...}里面的fun是fun(n,o)吗?明白这个区别后后面就容易多了。

好了,明白大概的原理后,我们来分析这个问题:

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

执行a = fun(0),0是作为n传入的,o没有参数传入,所以输出undefined,之后,

a = {
  fun:function(m){
     return fun(m,0);//这里的n应该变成了第一次调用时的参数n,也就是0
  }
}
也就是
a = {
 fun:function(m){
     return function(n = m, o = 0) {
         console.log(o) //输出0
         return {
            fun:function(m){
              return fun(m,n);
            }
          };
    };
  }
}

所以无论传入的m是什么,输出永远都是0
最后,输出结果如下

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

搞清楚第一个的过程,第二个的破解关键就在于闭包让函数内部的变量始终保存在内存之中。

//b
undefined
0
1
2
//c
undefined
0
1
1
参考文章
《学习Javascript闭包(Closure) - 阮一峰的网络日志》
《闭包 - 廖雪峰的官方网站》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108532.html

相关文章

  • 前端芝士】详解CSS盒模型、BFC、OffsetWidth&ClientWidth&am

    摘要:提供了糟糕的支持,而虽然接近标准,但依旧未能完全正确的支持标准。尽管修复了许多的问题,但是依然延续实现中的其它故障主要是盒模型问题。因此大部分的声明将触发严格模式即依据标准的规则渲染网页。 深入理解CSS盒模型、BFC、OffsetWidth&ClientWidth&ScrollWidth 本文将从盒模型开始,一步步涉及一些常见的前端笔试和面试点 主要参考自第一篇文章,然而笔者在读的时...

    binta 评论0 收藏0
  • 前端芝士】Javascript的原型与原型链

    摘要:在创建对象不论是普通对象还是函数对象的时候,都有一个叫做的内置属性,用于指向创建它的构造函数的原型对象,也就是。因为一个普通对象的构造函数所以原型链原型链的形成是真正是靠而非。参考文章最详尽的原型与原型链终极详解,没有可能是。 【前端芝士树】Javascript的原型、原型链以及继承机制 前端的面试中经常会遇到这个问题,自己也是一直似懂非懂,趁这个机会整理一下 0. 为什么会出现原型和...

    yy736044583 评论0 收藏0
  • 浅谈reacthooks闭包陷阱

      本文不会过多讲解基础知识,更多说的是在使用useRef如何能摆脱 这个 闭包陷阱 ?  react hooks 的闭包陷阱 基本每个开发员都有遇见,这是很令人抓狂的。  (以下react示范demo,均为react 16.8.3 版本)  列一个具体的场景:  functionApp(){   const[count,setCount]=useState(1);   useEffect(()=...

    3403771864 评论0 收藏0
  • Deep in JS - 收藏集 - 掘金

    摘要:今天同学去面试,做了两道面试题全部做错了,发过来给道典型的面试题前端掘金在界中,开发人员的需求量一直居高不下。 排序算法 -- JavaScript 标准参考教程(alpha) - 前端 - 掘金来自《JavaScript 标准参考教程(alpha)》,by 阮一峰 目录 冒泡排序 简介 算法实现 选择排序 简介 算法实现 ... 图例详解那道 setTimeout 与循环闭包的经典面...

    enali 评论0 收藏0

发表评论

0条评论

awokezhou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<