闭包的作用

闭包:内部函数保存到外部

当内部函数被保存到外部时,将会生成闭包。
闭包会导致原有作用域链不释放,造成内存泄漏(内存占用)

闭包的作用

  1. 实现公有变量: eg:函数累加器
  2. 可以做缓存(存储结构):eg:eater
  3. 可以实现封装,属性私有化:eg:new Person();
  4. 模块化开发,防止污染全局变量

累加器:

function add(){
    var count=0;
    function demo(){
        count++;
        console.log(count);
    }
    return demo;
}
var counter=add();
counter();->输出1
counter();->输出2

缓存:

function  eater(){
    var food="apple";
    var obj={
        eat:function (){
            if(food!=""){
                console.log("i am eating "+ food);
                food="";
            }else{
                console.log("eat emtpy ");
            }
        }
        push:function(myFood){
            food = myFood;
        }
    }
    return obj;
}
var eat1= eater();
eat1.eat();->输出apple
eat1.eat();->输出empty
eat1.push('banana');
eat1.eat();->输出banana

私有化:下面例子输入deng.prepareWife是undefind 形成了私有化变量

function Deng(name,wife){
//正常情况 函数里的var对象 函数执行完了就会被销毁,但是在这里因为被this.divorce的函数使用被返回了形成了闭包,所以无法销毁。
    var prepareWife="xiaozhang";
    this.name=name;
    this.wife=wife;
    this.divorce=function(){
        this.wife=prepareWife;
    }
    this.changePrepareWife=function(target){
        prepareWife=target;
    }
    this.sayPraprewife=function(){
        console.log(prepareWife);
    }
}
var deng=new Deng('deng','xiaoliu');

立即执行函数解决闭包作用域问题:

立即执行函数(执行完立即销毁) 针对初始化功能的函数
定义:此类函数没有生命,在一起执行过后释放。适合做初始化工作。

function test(){
    var arr=[];
    for(var i=0;i<10;i++){
        (function (j){
            arr[j]=function(){
            document.write(j+" ");//输为0,1,2,3,4,5,6,7,8,9
            }
        }(i));//用立即执行函数(立即执行函数也可以生成自己的作用域) i作为参数传给j j不会随着i改变而改变
    }
    return arr;
}
var myArr=test();
for(var j=0;j<10;j++){
        myArr[j]();
    }
<ul>
  <li id="myli">a</li>
  <li id="myli">a</li>
  <li id="myli">a</li>
</ul>
<script type="text/javascript">
function test(){
  var liList=document.getElementsByTagName("li");
  for (i  = 0; i < liList.length; i++) {
    (function(j){
      liList[j].onclick=function(){
      console.log(j);   //输出1 2 3   如果不用立即执行函数会报错
    }
    }(i))
  }
}
test();

下面内容引用自:
https://www.cnblogs.com/smyhvae/p/8578716.html

闭包的应用:定义具有特定功能的js模块

  • 将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数。

  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能。

方式一
(1)myModule.js:(定义一个模块,向外暴露多个函数,供外界调用)

function myModule() {
    //私有数据
    var msg = 'Smyhvae Haha'

    //操作私有数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase()); //字符串大写
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase()) //字符串小写
    }

    //通过【对象字面量】的形式进行包裹,向外暴露多个函数
    return {
        doSomething1: doSomething,
        doOtherthing2: doOtherthing
    }
}

上方代码中,外界可以通过doSomething1和doOtherthing2来操作里面的数据,但不让外界看到。

(2)index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>05_闭包的应用_自定义JS模块</title>
</head>
<body>
<!--
##  闭包的应用 : 定义JS模块
  * 具有特定功能的js文件
  * 将所有的数据和功能都封装在一个函数内部(私有的)
  * 【重要】只向外暴露一个包含n个方法的对象或函数
  * 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
    var module = myModule();
    module.doSomething1();
    module.doOtherthing2();
</script>
</body>
</html>

方式二
同样是实现方式一种的功能,这里我们采取另外一种方式。

(1)myModule2.js:(是一个立即执行的匿名函数)

(function () {
    //私有数据
    var msg = 'Smyhvae Haha'

    //操作私有数据的函数
    function doSomething() {
        console.log('doSomething() ' + msg.toUpperCase())
    }

    function doOtherthing() {
        console.log('doOtherthing() ' + msg.toLowerCase())
    }

    //外部函数是即使运行的匿名函数,我们可以把两个方法直接传给window对象
    window.myModule = {
        doSomething1: doSomething,
        doOtherthing2: doOtherthing
    }
})()

(2)index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>05_闭包的应用_自定义JS模块2</title>
</head>
<body>
<!--
闭包的应用2 : 定义JS模块
  * 具有特定功能的js文件
  * 将所有的数据和功能都封装在一个函数内部(私有的)
  * 只向外暴露一个包信n个方法的对象或函数
  * 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
-->

<!--引入myModule文件-->
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
    myModule.doSomething1()
    myModule.doOtherthing2()
</script>
</body>
</html>

上方两个文件中,我们在myModule2.js里直接把两个方法直接传递给window对象了。于是,在index.html中引入这个js文件后,会立即执行里面的匿名函数。在index.html中把myModule直接拿来用即可。

总结:

当然,方式一和方式二对比后,我们更建议采用方式二,因为很方便。

但无论如何,两种方式都采用了闭包。

闭包的缺点及解决

缺点:函数执行完后, 函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄露。

**解决:能不用闭包就不用,及时释放。**比如:

f = null;  // 让内部函数成为垃圾对象 -->回收闭包

总而言之,你需要它,就是优点;你不需要它,就成了缺点。

内存泄漏内存溢出

内存泄漏

内存泄漏:占用的内存没有及时释放。内存泄露积累多了就容易导致内存溢出。

常见的内存泄露:

  1. 意外的全局变量

  2. 没有及时清理的计时器或回调函数

  3. 闭包

情况1举例:

    // 意外的全局变量
    function fn() {
        a = new Array(10000000);
        console.log(a);
    }

    fn();

情况2举例:

    // 没有及时清理的计时器或回调函数
    var intervalId = setInterval(function () { //启动循环定时器后不清理
        console.log('----')
    }, 1000)

    // clearInterval(intervalId);  //清理定时器

情况3举例:

<script type="text/javascript">
  function fn1() {
    var arr = new Array[100000];   //这个数组占用了很大的内存空间
    function fn2() {
      console.log(arr.length)
    }
    return fn2
  }
  var f = fn1()
  f()

  f = null //让内部函数成为垃圾对象-->回收闭包
</script>

内存溢出(一种程序运行出现的错误)

内存溢出:当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误。

  //内存溢出
  var obj = {}
  for (var i = 0; i < 10000; i++) {
    obj[i] = new Array(10000000);   //把所有的数组内容都放到obj里保存,导致obj占用了很大的内存空间
    console.log('-----')
  }
### 闭包在编程中的作用用途 #### 1. **保存状态** 闭包能够保存函数的状态,使得即使外部函数已经执行完毕,内部变量依然存在并可被访问。这种能力让闭包成为一种非常有用的工具来管理程序中的状态[^3]。 ```javascript function createCounter() { let count = 0; // 局部变量 return function () { // 返回一个闭包 return count++; // 访问外部函数的局部变量 }; } const counter = createCounter(); console.log(counter()); // 输出 0 console.log(counter()); // 输出 1 ``` 通过这种方式,`createCounter` 创建了一个计数器,每次调用 `counter()` 都会增加 `count` 的值,并返回当前的结果。这展示了闭包如何保持对外部变量的引用[^5]。 --- #### 2. **实现私有化** 闭包可以帮助隐藏某些变量或逻辑,从而防止全局污染或者意外修改。由于闭包可以访问外部函数的作用域,但它本身无法直接从外界访问到这些变量,因此实现了某种程度上的“私有化”[^4]。 ```javascript function makePrivateData(data) { return function (action, value) { if (action === 'get') { return data; } else if (action === 'set' && typeof value !== undefined) { data = value; } }; } const privateDataHandler = makePrivateData(10); console.log(privateDataHandler('get')); // 获取数据 -> 10 privateDataHandler('set', 20); // 设置新值 console.log(privateDataHandler('get')); // 再次获取 -> 20 ``` 这里定义了一种机制,只有通过特定接口才能读取或更改存储的数据[^3]。 --- #### 3. **延迟计算与惰性求值** 闭包可用于推迟某项操作直到实际需要的时候才去完成它。这种方法通常被称为懒加载(Lazy Loading),有助于优化性能,尤其是在处理复杂运算时[^1]。 ```javascript function lazySum(a, b) { const sum = a + b; return function () { console.log(`The result is ${sum}`); }; } const calculateLater = lazySum(7, 8); setTimeout(calculateLater, 2000); // 延迟两秒打印 The result is 15 ``` 此代码片段展示的是,在初次调用 `lazySum` 后并不会立即显示结果;而是等到触发回调时才会展现最终数值[^4]。 --- #### 4. **构建高阶函数** 高阶函数是指接受其他函数作为参数或将函数作为输出的一种形式。借助于闭包,我们可以轻松制造出各种各样的动态行为生成器,比如事件处理器、定时器等等[^2]。 ```ruby def timer_factory(message) lambda do |seconds| sleep(seconds) puts "#{message} after #{seconds} seconds" end end timer = timer_factory("Hello world!") timer.call(3) # 程序暂停三秒钟后再输出消息 ``` 上述 Ruby 示例说明了怎样利用闭包创建自定义延时通知的功能[^2]。 --- #### 5. **模拟类模块模式** 尽管 JavaScript 并未原生支持传统意义上的 OOP 类型继承模型,但是凭借闭包却能模仿出类似的结构特征。 ```javascript const Module = (() => { let _state = {}; // 私有成员 return { setState(key, val) {_state[key] = val}, getState(key) {return _state[key]} }; })(); Module.setState('name', 'John Doe'); console.log(Module.getState('name')); // John Doe ``` 这段脚本运用 IIFE 即刻执行表达式配合闭包技术搭建起了简易版模块框架。 --- ### 结论 综上所述,闭包不仅限于简单的嵌套关系体现,更是在现代软件开发过程中扮演着不可或缺的角色。无论是为了保护敏感信息还是提升应用效率,合理运用闭包都能带来显著优势。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值