JavaScript高级之闭包详解

目录
  • 1. 闭包的概念
    • 知识点的补充:
  • 2. 闭包的作用:
  • 3. 闭包示例
    • 3.1 点击li,输出当前li的索引号
  • 总结

1. 闭包的概念

来看一般函数的执行和启发:

        function stop() {
            var num = 0;
            console.log(num);
        }
        stop(); // 打印出来num是0
        console.log(num); // 报错 函数未定义

1. 此时,函数的外部无法访问函数内部的变量

2. 函数内部定义的变量不会一直存在,随着函数的运行结束而消失

闭包的概念:

1. 是一个函数,这个函数有权访问另一个作用域中的变量。

2. 另一种说法,当内部函数的生命周期大于外部函数的声明周期,而内部函数以某一种方式被外部作用域访问时,闭包就产生了。

来看如下闭包的代码和解释:

        function fn() {
            var num = 10;
            // function fun() {
            //     console.log(num);

            // }
            // return fun;
            return function () {
                console.log(num); // 10
            }
        }
        var f = fn();
        f();

我们可以拆解为几个部分:

1. fn函数里面有内部的返回值且就是一个函数。

2. return的这个函数内部打印了num变量。为什么能够打印num变量,原因在于作用域链的访问机制,下面会补充作用域和作用域链的知识点。

3. 我们在外部用f变量接受了fn(),也就是接受了fn的返回值【内部函数】

4. 紧接着调用f,也就是调用了fn里面的内部函数。最终能够打印10

知识点的补充:

1. 作用域:

变量在某个范围内起作用,超出了这个范围,就不起作用。这个范围就是作用域。作用域在函数的定义时就产生,而不是函数调用时产生的。

2. 作用域链:

一句话概括:根据【内部函数可以访问外部函数变量】,采用就近原则一层一层向上查找变量,这个机制就叫作作用域链。

函数A包含了函数B,那么函数B就是函数A的内部函数,

而内部函数如果要使用一个变量,首先看自己内部有没有这个变量,

如果没有,就会去紧挨着的上一级查找,【就近原则】

如果函数一层一层都找不到,最后才会去全局变量下面找。

        var a = 1;
        var b = 11;
        function fn1() {
            var a = 2;
            var b = '22';
            fn2();
            function fn2() {
                var a = 3;
                fn3();
                function fn3() {
                    var a = 4;
                    console.log(a); // 4
                    console.log(b); // '22'
                }
            }
        }
        fn1();

3. 垃圾回收机制

可以参考这位大哥对于JS垃圾回收机制的描述:

//www.jb51.net/article/229425.htm

我们结合这三个概念看闭包的作用

2. 闭包的作用:

我们把函数A叫作外层的函数,这个函数内部有一个函数B。

外部用一个变量f接受函数A的返回值【函数B】

而函数A作用域的变量叫作num

1. 能够在函数的外部访问函数内部的变量【搭建外部访问内部作用域的通道】

原理:上面其实有解释过。

第一要理解,作用链的原理看上面。函数B能够调用函数A的变量num

第二要理解,首先函数A的返回值是函数B【内部函数】,其次这个返回值要在函数外部用变量f接受,接受以后就能够调用函数B,函数B就会访问函数A的变量num。而这个内部函数B就是闭包函数啦。

2. 能够延长函数内部变量的生命周期。

第一个作用带来第二个作用。js的变量存在垃圾回收机制,如果函数执行完毕,变量会被清除,内存也会消除。可是如果利用闭包,变量可以不被立即清除。

原因是,外部的变量f接受了一个函数A的内部函数B,而这个内部函数访问了函数A作用域的变量num,只要函数B执行且变量f一直存在,那么变量num就会一直存在。不会因为函数A的执行结束就消失。

参考了下面的文章,讲的非常详细,推荐看。

JavaScript闭包详解

3. 闭包示例

后面会补充闭包的一些应用。

我们要想起什么场合用闭包,闭包不能滥用。

3.1 点击li,输出当前li的索引号

    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
    <script>
        // 闭包应用-点击li输出当前li的索引号
        // 1. 我们可以利用动态添加属性的方式
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            lis[i].onclick = function () {
                console.log(i); // 四个4
            }
        }
    </script>

原理:上图这样写,打印出来的i永远都是4。原因是,此时首先是非严格模式,在非严格模式下,for循环是同步执行任务,而按钮点击再执行是异步任务,同步执行完毕,i加到了4.再执行异步任务打印i,都是4。

改法1:用闭包

1.for循环生成四个立即执行函数

2. 立即执行函数是闭包的一种应用。立即执行函数里面的所有函数包括【点击 回调】函数都可以使用立即执行函数的传递的形参。

        for (var i = 0; i < lis.length; i++) {
            (function (i) {
                // console.log(i);
                lis[i].onclick = function () {
                    console.log(i);

                }
            })(i);
        }

改法2:var--->let

点击对应小li,打印i是对应索引号。使用let是ES6语法,此时for有块级作用域

        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (let i = 0; i < lis.length; i++) {
            lis[i].onclick = function () {
                // console.log(i);
                console.log(i);
            }
        }

改法3:用设置自定义属性index的方法

        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) { // 注意这里是var不是let
            lis[i].index = i; // 注意这里是lis[i]不是this.index,此时没有点击,哪里来的this
            lis[i].onclick = function () {
                console.log(this.index);
            }
        }

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 详解JavaScript闭包问题

    闭包是纯函数式编程语言的传统特性之一.通过将闭包视为核心语言构件的组成部分,JavaScript语言展示了其与函数式编程语言的紧密联系.由于能够简化复杂的操作,闭包在主流JavaScript库以及高水平产品代码中日益流行起来. 一.变量的作用域 在介绍闭包之前,我们先理解JavaScript的变量作用域.变量的作用域分为两种:全局变量和局部变量. 1.全局变量 var n = 999; //全局变量 function f1() { a = 100; //在这里a也是全局变量 alert(n);

  • Javascript作用域与闭包详情

    目录 1.作用域 2.作用域链 3.词法作用域 5.闭包的应用 6.闭包的缺陷 7.高频闭包面试题 1.作用域 简单来说,作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限 在ES5中,一般只有两种作用域类型: 全局作用域:全局作用域作为程序的最外层作用域,一直存在 函数作用域:函数作用域只有在函数被定义时才会被创建,包含在父级函数作用域或全局作用域中 说完概念,我们来看下面这段代码: var a = 100 function test(){ var b = a * 2 var

  • JavaScript 闭包的使用场景

    一.闭包 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,闭包就是能够读取其他函数内部变量的函数.所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁. 比如下面的代码: function f1() { var n = 999; function f2() { console.log(n); } return f2; } var result = f1(); result();//999 函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见

  • js闭包的9个使用场景

    1.返回值(最常用) //1.返回值 最常用的     function fn(){         var name="hello";         return function(){             return name;         }     }     var fnc = fn();     console.log(fnc())//hello 这个很好理解就是以闭包的形式将 name 返回. 2.函数赋值 var fn2; function fn(){   

  • 一文了解JavaScript闭包函数

    目录 变量作用域 闭包的概念 闭包的用途 闭包的缺点 最后总结一下闭包的好处与坏处 总结 变量作用域 要理解JavaScript闭包,就要先理解JavaScript的变量作用域. 变量的作用域有两种:全局的和局部的(全局变量和局部变量) JavaScript中,在函数内部可以直接读取到全局变量. var n=10 function fn(){ alert(n) } fn() //10 而在函数外部无法读取到函数内部的变量. function fn(){ var n=10; } fn() aler

  • JavaScript高级之闭包详解

    目录 1. 闭包的概念 知识点的补充: 2. 闭包的作用: 3. 闭包示例 3.1 点击li,输出当前li的索引号 总结 1. 闭包的概念 来看一般函数的执行和启发: function stop() { var num = 0; console.log(num); } stop(); // 打印出来num是0 console.log(num); // 报错 函数未定义 1. 此时,函数的外部无法访问函数内部的变量 2. 函数内部定义的变量不会一直存在,随着函数的运行结束而消失 闭包的概念: 1.

  • javascript 闭包详解及简单实例应用

    JS 闭包详解及实例: 最近学习JS的基础知识,学习了闭包的知识点,有很多疑惑,这一段时间还是一直有在看闭包的相关知识理解就更深入了一点,下面说说我的理解. function fn(){ var a = 0; return function (){ return ++a; } } 如上所示,上面第一个return返回的就是一个闭包,那么本质上说闭包就是一个函数.那么返回这个函数有什么用呢? 那是因为这个函数可以调用到它外部的a这个变量.其实也就是说,return返回的是这个函数 + a这个变量

  • JavaScript里 ==与===区别详解

    1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较"转化成同一类型后的值"看"值"是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进行"值"比较,两者结果一样 2.对于Array,Object等高级类型,==和===是没有区别的 进行"指针地址"比较 3.基础类型与高级类型,==和===是有区别的 1)对于==,将高级转化为基础类型,进行"值&quo

  • javascript中正则表达式语法详解

    好久都没有写博客了,主要是太懒了,尤其是在阳春三月,风和日丽的日子,太阳暖暖的照在身上,真想美美的睡上一觉.就导致了这篇博客拖到现在才开始动笔,javascript的正则这一块也不是什么新的东西,主要是以前本人一遇到写正则的需求就开始头大,头疼,网上剽窃,东拼西凑,反正就是各种不适应,所以我打算系统的把正则表达式看一遍,一来是自己有所提升,这一块知识点的查漏补缺,二来是给大家分享一下.好了,下面我们直接进入主题: 正则是匹配字符串特定模式的一种表达式,官方是这样说的,但我的理解不外乎就是匹配字符

  • 基于JavaScript表单脚本(详解)

    什么是表单? 一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包含了文本框.密码框.隐藏域.多行文本框.复选框.单选框.下拉选择框和文件上传框等. 表单按钮:包括提交按钮.复位按钮和一般按钮:用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作. JavaScript与表单间的关系:JS最初的应用就是用于分担服务器处理表单的责任,打破依赖服务器的局面,尽管目前web和jav

  • JavaScript数据结构链表知识详解

    最近在看<javascript数据结构和算法>这本书,补一下数据结构和算法部分的知识,觉得自己这块是短板. 链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 好处:可以添加或移除任意项,它会按需扩容,且不需要移动其他元素. 与数组的区别: 数组:可以直接访问任何位置的任何元素: 链表:想要访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素. 做点小笔记. funct

  • python中函数总结之装饰器闭包详解

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器就是包装原来的函数,从而在不需要修改原来代码的基础之上,可以做更多的事情. 装饰器语法如下: @deco2 @deco1 def func(arg1,arg2...): pass 这个表示了有两个装饰器的函数,那么表示的含义为:func = deco2(deco1(func)) 无参装饰器语法如下:

  • JavaScript String 对象常用方法详解

    字符串是不可变的.字符串方法,不会改变原有字符串的内容,而是返回一个新字符串. charAt():返回在指定位置的字符. concat():连接两个字符串文本,并返回一个新的字符串. indexOf(): indexOf() 方法返回指定值在字符串对象中首次出现的位置. match(): 使用正则表达式与字符串相比较. replace(): 被用来在正则表达式和字符串直接比较,然后用新的子串来替换被匹配的子串. slice(): 摘取一个字符串区域,返回一个新的字符串. split(): 通过分

  • JavaScript hasOwnProperty() 函数实例详解

    hasOwnProperty()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性.如果有,返回true,否则返回false. 该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法. IE 5.5+.FireFox.Chrome.Safari.Opera等主流浏览器均支持该函数. 语法 object.hasOwnProperty( propertyName ) 参数 参数 描述 propertyName Str

  • 基于Python函数的作用域规则和闭包(详解)

    作用域规则 命名空间是从名称到对象的映射,Python中主要是通过字典实现的,主要有以下几个命名空间: 内置命名空间,包含一些内置函数和内置异常的名称,在Python解释器启动时创建,一直保存到解释器退出.内置命名实际上存在于一个叫__builtins__的模块中,可以通过globals()['__builtins__'].__dict__查看其中的内置函数和内置异常. 全局命名空间,在读入函数所在的模块时创建,通常情况下,模块命名空间也会一直保存到解释器退出.可以通过内置函数globals()

随机推荐