浅谈闭包问题

生活博客

这周暂停了视频项目,稍微留下点时间去思考近期的状态和目标,自从小组招新开始,状态好像一直不在线。但需要肯定的是我需要去不断调整自己的学习方法和学习习惯,不能再像原来一样懒散,要学会自己去找东西学,向更深层次的学习。

这周还进行了学校官网的更改,体验了一次“乙方”。也同时切实体会到代码的复用性和可维护性的重要性和价值实在。

闭包

什么是闭包?

官网给出的解释是

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

em ~

有点难懂,我认为的闭包是

一个嵌套的内部函数引用了嵌套的外部函数的参数或者是变量就形成了闭包

下面是一个简单的闭包实例

function exterior() {
    let str = "今天天气不错";
    function interior() {
        console.log(str);
    }
    interior();
}
exterior();// 今天天气不错

 在内部函数interior中调用了外部函数的str变量就形成了一个简单的闭包

使用闭包有什么好处?

在学习闭包的开始,我就有一个疑问,为什么非要进行函数之间的嵌套去写一个闭包,还要把内部函数要使用的参数放到外部函数中声明,再调用。我直接声明一个公共变量再写一个函数调用,它不香吗?

 那就要聊一聊闭包的好处了

  1. 数据封装:闭包将数据和函数封装在一个作用域内部,从而实现减少对外部的暴露细节。换句话说就是通过闭包,可以创建一些私有的变量和函数,而这些函数和变量只能通过内部函数去引用或访问,外部无法直接访问,这种封装性可以帮助我们设计更加模块化、可维护和安全的代码。

// 不使用闭包的情况(全局变量污染)
// 全局污染示例
let name = "John"; // 全局变量

function greet() {
  console.log("Hello, " + name);
}

function updateName(newName) {
  name = newName; // 修改全局变量
}

greet(); // 输出: Hello, John
updateName("Alice");
greet(); // 输出: Hello, Alice
// 使用闭包
function createPerson() {
    let name = "Jhon"; // 外部函数的变量
    let age = 18;
    function person() {
        age++; // 内部函数访问外部函数的变量
        console.log(name, age);
    }

    return person;
}

let person = createPerson(); // 创建闭包
person(); // 输出: Jhon 19
person(); // 输出: Jhon 20
let name = "Jhon"
// 外部函数无法改变内部函数的参数
function changeName(newName) {
    name = newName;
}
changeName("Tom");
console.log(name); // Tom
person(); // Jhon 21

2.保持状态:闭包可以使函数在调用之间保持其状态。通过将变量保存在闭包的环境中,即使函数执行完毕并被销毁,闭包仍然保留对这些变量的引用。

// 使用闭包制作一个简单的计数器
function getNum() {
    let num = 0;
    function addNum() {
        num++;
        console.log(num);
    }
    return addNum;
}
let count = new getNum();
count(); // 1
count(); // 2
count(); // 3
// 在上述代码中通过使用调用count()函数
// 实现num变量的增加,可以观察到在每次调用函数的过程中
// num变量都是在原数值的基础上进行增加的
// 这也就解释了闭包在执行完毕后仍然保留变量的引用

3.实现函数工厂和高阶函数

在学习这个优点之前我们先了解一下什么是函数工厂和高阶函数

函数工厂 

 函数工厂是一个返回函数的函数,它可以根据不同的参数或配置生成具有不同行为的函数

function createPerson(name) {
    return function(age) {
      console.log(`${name}, ${age}`);
    }
  }
  
  const personJhon = createPerson('Jhon');
  const personTom= createPerson('Tom');
  // 第一个参数录入的是name
  personJhon(18); // Jhon, 18
  personTom(20); // Tom, 20
// 第二个参数录入的是age

很明显上述内部函数引用了外部函数的参数name ,并且有增加了一个新的参数age,在内部函数实现对内部参数以及外部函数的参数进行处理,有必要一提的是函数工厂的返回值是一个函数(满足了闭包产生的条件)。

高阶函数

高阶函数是指接受一个或多个函数作为参数,返回一个函数的函数

与普通函数不同的是高阶函数的参数一般都是函数

function withLogging(func) {
  return function(...args) {
    console.log('Calling function:', func.name);
    const result = func(...args);
    console.log('Function result:', result);
    return result;
  };
}

function add(a, b) {
  return a + b;
}

const loggedAdd = withLogging(add);
console.log(loggedAdd(2, 3)); // 输出:Calling function: add,Function result: 5

闭包使得函数工厂和高阶函数的实现变得简单而灵活。通过利用闭包的状态保持特性,我们可以轻松地创建具有不同行为的函数,并将函数作为参数传递给其他函数。

使用闭包有什么缺点?
  1. 内存消耗:闭包会导致额外的内存消耗。由于闭包保留了函数的作用域链,它会使得函数中的变量无法被垃圾回收,即使函数执行完毕后也不会释放内存。

  2. 性能影响:由于闭包涉及到作用域链的查找,会对函数执行速度产生一定的性能损失。每次访问闭包中的变量,都需要沿着作用域链进行查找,这比直接访问局部变量或全局变量要慢一些。

  3. 隐式的状态共享:闭包可以访问外部函数的变量,这样就可以实现状态共享。如果多个闭包共享同一个变量,并对其进行修改,可能会产生意料之外的结果。

  4. 内存泄漏风险:当闭包中引用了外部函数的变量,并且这个闭包长时间存在时,外部函数的变量无法被垃圾回收。如果不注意释放闭包或手动解除对外部变量的引用,可能会导致内存泄漏问题。

 总结

尽管闭包具有上述优点,但在使用闭包时也需要注意一些潜在的问题,如内存消耗、性能影响、隐式的状态共享和内存泄漏风险。因此,在编写代码时,应谨慎使用闭包,并遵循最佳实践,以确保代码的正确性、性能和可维护性。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值