广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JavaScript闭包怎么理解
  • 800
分享到

JavaScript闭包怎么理解

2024-04-02 19:04:59 800人浏览 泡泡鱼
摘要

这篇文章主要介绍“javascript闭包怎么理解”,在日常操作中,相信很多人在JavaScript闭包怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaScri

这篇文章主要介绍“javascript闭包怎么理解”,在日常操作中,相信很多人在JavaScript闭包怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaScript闭包怎么理解”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

JavaScript闭包怎么理解

从定义上来讲,它是一个脚本语言,而且是一个相对容易学习的脚本语言。不需要太多的专业知识,你也能够在一定程度上使用js(JavaScript的简写)代码。

1. 学习JavaScript的契机

正式学习JavaScript是在培训班,没错我是从培训班出来的,并不是科班出身,可以说是非常的草根了。我学习的时候es6标准还并未普及,变量命名还在用非常传统的var,学习的第一段代码是经典的console.log('Hello,world!'),当然它是在控制台上打印出来的。

当然,在培训机构中的JavaScript内容讲的是非常的浅显,只有最为基础的变量定义与命名,function声明,回调函数,ajax以及最为基础的dom操作。显然这些内容对于工作完全不够用的。

对于js学习的‘进修’机会来源于我的工作,在工作中我第一次知道了node这个东西,也了解到即便是js也是可以做后台的(我是做的JAVA培训),也开始逐渐接触到了一些ES6的标准。当然这些都是后话,最开始我接触到最大的障碍是这货。

2.‘恶心’的闭包

啊,对我只有那么一丁丁点基础的我,完全无法理解我们公司自己封装的JSONp代码,它是长这个样子的。

  var jsonp = (function(){
        var JSONP;
       return function(url){
           if (JSONP) {
             document.getElementsByTagName("head")[0].removeChild(JSONP);
          }
         JSONP = document.createElement("script");
          JSONP.type = "text/javascript";
          JSONP.src = url;
          document.getElementsByTagName("head")[0].appendChild(JSONP);
       }
     }())

当然,现在浏览器上已经无法通过控制台直接使用这个方法了,为了防止XSS攻击浏览器已经禁止这样注入代码了,但是在服务器上还是可以用的,当然,这些都不是重点。

重点是这里

    if (JSONP) {
       //dosome
 }

如果你和我当初一样,不知道什么叫闭包或者对闭包一知半解,那么,对于这里你应该也会产生疑问,思路大约是这样的

第2行定义了JSONP但是没有赋值,现在JSONP值为null,第三行返回了一个方法,第四行检测JSONP值是否为空,如果不为空则做了一些事情,好了,后面可以不用看了,这个if白写了,它百分百进不去!

你看嘛,前面也没有赋值,然后直接判断,那它明明就是null。但是实际使用的时候你会发现,这个地方第一次调用确实不会进入这个分支,但只要你调用了第二次,,它就百分百会进入这个分支。

// 这个是一个可以在控制台输出的闭包版本,你可以自己试一下
var closedhull = (function() {
    let name = null; // 这里直接赋值为null
    return function(msg){
        if(name) {
            console.log('name:', name)
            return name += msg;
        }
        return name = msg;
    }
}())
closedhull('我是第一句。') //我是第一句。
closedhull('我是第二句。') //我是第一句。我是第二句。

上面这个例子运行后,无论是从console.log()亦或是返回值上都不难看出,确实进入了if(name)的分支,这个就是闭包的表现。这里给出一下闭包的定义

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

3.闭包的样子到底是什么样的

好了,看过闭包是个啥了,先不说会不会用,至少,算是见过了,闭包有个显著的特征return function(){}

不是!

它的显著特征是在function内的function!

观察以下方法


function test1(){
    // a应该在方法运行结束后销毁
    let a = 1;
    return {
        add: function(){
            return ++a;
        }
    }
}
let a = test1();
a.add()//2
a.add()//3

(function(){
    // b应该在方法运行结束后销毁
    let b = 1,
        timer = setInterval(()=>{
        console.log(++b)
    }, 2000)
    setTimeout(()=>{
        clearInterval(timer)
    }, 10000)
})()// 2 3 4 5 6

function showMaker(obj){
    // obj应该在方法运行结束后销毁
    return function(){
        console.log(JSON.stringify(obj))
    }
}
let shower = showMaker({a:1})
// 显然这里你还能看到他
shower(); // {"a":1}

let outObj = (function(){
    let c = 'hello',
        obj = {};
    Object.defineProperty(obj, 'out', {
        get(){
            return c;
        },
        set(v){
            c = v;
        }
    });
    return obj
})()
outObj.out // 可以读取并设置c的值

这四个都是闭包,他们都具备方法中的方法这一特性。

4.闭包与方法栈(对原理不感兴趣可以略过)

闭包的定义,1. 可以在变量的作用域外访问该变量。2. 通过某种手段延长一个局部变量的生命周期。3. 让一个局部变量的存活时间超过它的时间循环执行时间

3中由于涉及到了事件循环概念,之后涉及到时会去讲的,这里主要讨论前两种方式的定义。

一下内容如果你知道方法栈是个啥了就可以跳过了

局部作用域:在ES6之前,一般指一个方法内部(从参数列表开始,到方法体的括号结束为止),ES6中增加let关键字后,在使用let的情况下是指在一个{}中的范围内(显然,你不能在隐式的{}中使用let,编译器会禁止你做出这种行为的,因为没有{}就没有块级作用域),咱们这里为了简化讨论内容,暂且不把let的块级作用域算作闭包的范畴(其实应该算,不过意义不大,毕竟,你可以在外层块声明它。天啊,JS的命名还没拥挤到需要在一个方法内再去防止污染的程度。)

局部变量:区别于全局变量,全局变量会在某些时候被意外额创造和使用,这令人非常的...恼火和无助。局部变量就是在局部作用域下使用变量声明关键字声明出来的变量,应该很好理解。

局部变量的生命周期:好了,你在一个局部作用域中通过关键字(var const let等)声明了一个变量,然后给它赋值,这个局部变量在这个局部作用域中冒险就开始了,它会被使用,被重新赋值(除了傲娇的const小姐外),被调用(如果它是个方法),这个局部变量的本质是一个真实的值,区别在于如果它是个对象(对象,数组,方法都是对象)那么,它其实本质是一个地址的指针。如果它一个基础类型,那么它就是那个真实的值。它之所以存活是因为它有个住所。内存。

局部作用域与内存:每当出现一个局部作用域,一个方法栈就被申请了出来,在这个方法栈大概长这样子

|  data5 |
|  data4 |
|  data3 |
|  data2 |
|__data1_|

当然,它是能够套娃的,长这个样子

|  | d2 |  |
|  |_d1_|  |
|  data3   |
|  data2   |
|__data1___|

如果上面的东西是在太过于抽象,那么,我可以用实际案例展示一下

function stack1(){
    var data1,
        data2,
        data3,
        data4,
        data5
}
function stack2(){
    var data1,
        data2,
        data3;
    function stackInner(){
        var d1,
            d2;
    }
}

如果方法栈能够直观的感受的话,大约就是这个样子,咱们重点来分析stack2的这种情况,同时写一点实际内容进去

function stack2(){
    var data1 = '1',
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = '4',
            d2 = {y: '5'};
    }
    stackInner()
}
stack2()

显然其中data1,data3,d1持有的是基本类型(string),data2,d2持有的是引用类型(object),反应到图上

运行时的方法栈的样子

            |------>{y: '5'}
            |    |->{x: '2'}
    |  | d2-|   || |
    |  |_d1='4'_|| |
    |  data3='3' | |
    |  data2 ----| |
    |__data1='1'___|

画有点抽象...就这样吧。具体对象在哪呢?他们在一个叫堆的地方,不是这次的重点,还是先看方法栈内的这些变量,运行结束后,按照先进后出的原则,把栈内的局部变量一个一个的销毁,同时堆里的两个对象,由于引用被销毁,没了继续存在的意义,等待被垃圾回收。

接下来咱们要做两件事情:

  • d1不再等于4了,而是引用data1

  • return stackInner 而不是直接调用

这样闭包就完成了

function stack2(){
    var data1 = {msg: 'hello'},
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = data1,
            d2 = {y: '5'};
    }
    return stackInner
}
var out = stack2()

这里有一个要点,d2赋值给data1一定是在stackInner中完成的,原因?因为再stackInner方法中d2才被声明出来,如果你在stack2中d1 = data1那么恭喜你,你隐式的声明了一个叫d1的全局变量,而且在stackInner由于变量屏蔽的原因,你也看不到全局上的d2,原本计划的闭包完全泡汤。

变量屏蔽:不同作用域中相同名称的变量就会触发变量屏蔽。

看看栈现在的样子

运行时的方法栈的样子

               |------>{y: '5'}
out<---|       | |----|
    |  |  | d2-| | |  |  |
    |  |--|_d1---|_|  |  |
    |     data3='3'   |  |
    |     data2(略)   |  |
    |_____data1<------|__|

好了,这个图可以和我们永别了,如果有可能,我后面会用画图工具替代,这么画图实在是太过邪典了。

这里涉及到了方法栈的一个特性,就是变量的穿透性,外部变量可以在内部的任意位置使用,因为再内部执行结束前,外部变量会一直存在。

由于stackInner被外部的out引用,导致这个对象不会随着方法栈的结束而销毁,接下来,最神奇的事情来了,由于stackInner这对象没有销毁,它内部d1依然保有data1所对应数据的引用,d1,d2一定会活下来,因为他们的爸爸stackInner活下来了,data1也以某种形式活了下来。

为什么说是某种形式,因为,本质上来说data1还是被销毁了。没错,只不过,data1所引用的那个对象的地址链接没有被销毁,这个才是本质。栈在调用结束后一定是会销毁的。但是调用本体(方法对象)只要存在,那么内部所引用的链接就不会断。

这个就是闭包的成因和本质。

5.闭包有什么用

OK,我猜测上一个章节估计很多人都直接跳过了,其实,跳过影响也不多,这个部分描述一下结论性的东西,闭包的作用。

它的最大作用就是给你的变量一个命名空间,防止命名冲突。要知道,你的框架,你export的东西,你import进来的东西,在编译的时候都会变成闭包,为的就是减少你变量对全局变量的污染,一个不依赖与import export的模块的代码大概长这个样子

(function(Constr, global){
    let xxx = new Constr(env1, env2, env3)
    global.NameSpace = xxx;
})(function(parm1, parm2, parm3) {
    //dosomeing
    reutrn {
        a: 'some1',
        b: 'some2',
        funcC(){
            //dosome
        },
        funcD(){
            //dosome
        }
    }
}, window)

当然这种封装代码的风格有多种多样的,但是大家都尽量把一套体系的内容都放到一个命名空间下,避免与其他框架产生冲突

到此,关于“JavaScript闭包怎么理解”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: JavaScript闭包怎么理解

本文链接: https://www.lsjlt.com/news/88653.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JavaScript闭包怎么理解
    这篇文章主要介绍“JavaScript闭包怎么理解”,在日常操作中,相信很多人在JavaScript闭包怎么理解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JavaScri...
    99+
    2022-10-19
  • 怎么理解javascript闭包
    本篇内容介绍了“怎么理解javascript闭包”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!第一梯队理解...
    99+
    2022-10-19
  • 怎么理解JavaScript闭包函数
    本篇内容介绍了“怎么理解JavaScript闭包函数”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!变量作用域要理解JavaScript闭包,...
    99+
    2023-06-25
  • JavaScript闭包如何理解
    这篇“JavaScript闭包如何理解”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Jav...
    99+
    2022-10-19
  • 怎样解析javascript的闭包
    今天就跟大家聊聊有关怎样解析javascript的闭包,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。什么是闭包:闭包是一个存在内部函数的引用关系。该引用指向的是外部函数的局部变量对象...
    99+
    2023-06-29
  • JavaScript闭包详解
    目录1.什么是闭包?1.2 闭包的记忆性:函数能够记住定义时所处的环境1.3 闭包现象:JS中每次创建函数时都会创建闭包2.闭包的功能:记忆性、模拟私有变量2.1 记忆性2.2 模拟...
    99+
    2022-11-12
  • JavaScript中闭包怎么用
    这篇文章将为大家详细讲解有关JavaScript中闭包怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。闭包真的是学过一遍又一遍,Js博大精深,每次学习都感觉有新的收获...
    99+
    2022-10-19
  • javascript怎么形成闭包
    这篇文章主要介绍了javascript怎么形成闭包的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇javascript怎么形成闭包文章都会有所收获,下面我们一起来看看吧。 ...
    99+
    2022-10-19
  • 如何理解并掌握JavaScript闭包
    这篇文章主要介绍“如何理解并掌握JavaScript闭包”,在日常操作中,相信很多人在如何理解并掌握JavaScript闭包问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何...
    99+
    2022-10-19
  • JavaScript闭包原理及作用详解
    目录简介闭包的用途柯里化实现公有变量缓存封装(属性私有化)闭包的原理垃圾收集简介实际开发中的优化简介 说明 本文介绍JavaScript的闭包的作用、用途及其原理。 闭包的定义 闭包...
    99+
    2022-11-12
  • JavaScript中怎么应用闭包
    JavaScript中怎么应用闭包,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。什么是闭包(Closure)简单讲,闭包就是指有权访问另一...
    99+
    2022-10-19
  • 详解JavaScript闭包问题
    闭包是纯函数式编程语言的传统特性之一。通过将闭包视为核心语言构件的组成部分,JavaScript语言展示了其与函数式编程语言的紧密联系。由于能够简化复杂的操作,闭包在主流JavaSc...
    99+
    2022-11-12
  • JavaScript内存管理与闭包实例详解
    目录1. 内存管理的理解1.1 认识内存管理1.2 JavaScript的内存管理2. 垃圾回收(GC)2.1 认识垃圾回收2.2 GC算法 – 引用计数2.3 GC算法...
    99+
    2022-11-13
  • JavaScript闭包是什么及怎么用
    这篇“JavaScript闭包是什么及怎么用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“JavaScript闭包是什么及怎...
    99+
    2023-07-04
  • 什么是JavaScript闭包
    本篇内容主要讲解“什么是JavaScript闭包”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是JavaScript闭包”吧!什么是闭包简言之,闭包是由函数...
    99+
    2022-10-19
  • JavaScript闭包是什么
    这篇文章给大家分享的是有关JavaScript闭包是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。概论在讨论ECMAScript闭包之前,先来介绍下函数式编程(与ECMA-2...
    99+
    2022-10-19
  • JavaScript高级之闭包详解
    目录1. 闭包的概念知识点的补充:2. 闭包的作用:3. 闭包示例3.1 点击li,输出当前li的索引号总结1. 闭包的概念 来看一般函数的执行和启发: func...
    99+
    2022-11-12
  • JavaScript闭包知识点整理
    这篇文章主要讲解了“JavaScript闭包知识点整理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JavaScript闭包知识点整理”吧!在谈闭包之前,我...
    99+
    2022-10-19
  • javascript闭包的模块化怎么做
    本篇内容主要讲解“javascript闭包的模块化怎么做”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript闭包的模块化怎么做”吧!说明闭包的重要使用是模块化,它可以将成员变量的命...
    99+
    2023-06-20
  • JavaScript中的闭包closure怎么使用
    这篇文章主要介绍“JavaScript中的闭包closure怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JavaScript中的闭包closure怎么使用”文章能帮助大家解决问题。闭包简述...
    99+
    2023-07-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作