JavaScript闭包-Closure

闭包的概念

       闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
       可以简单的理解为:闭包 = 内层函数引用了外层函数的变量
       其实只要支持函数式编程特性的语言都有闭包的概念,而闭包(Closure,译为:永久的关闭)之所以被称为闭包,是因为它“封闭”(close over)了在其内部创建时所处环境的状态(即变量)。这也引出了闭包的特性:私有化变量

使用闭包私有化变量

let a = 1
function badFn() {
    a++
}
function goodFn() {
    console.log(a) // 2
}
badFn()
goodFn()

       先看没有使用闭包的情况,可以看到badFn在goodFn调用前将a变量修改了,需求是goodFn不希望其他函数来修改a变量,我们可以使用闭包完成这一个需求。

let a = 1
function badFn() {
    a++
}
function outer() {
    let a = 1
    function goodFn() {
        console.log(a) // 1
    }
    goodFn()
}

badFn()
outer()

       我们给goodFn套一层outer函数并在outer函数中再定义一个a,现在goodFn有了属于他自己的a,外部环境访问不到outer中的a变量,这就实现了变量的私有化,不用担心badFn插手goodFn的a变量。

使用return让外部访问私有化的变量

       现在goodFn想在不被外部干扰a的情况下继续做一些事情,他想统计自己被调用的次数。

function outer() {
    let a = 1
    function goodFn() {
        a++
        console.log(a) 
    }
    return goodFn
}

const printA = outer()
printA() // 2
printA() // 3
printA() // 4
printA() // 5

       通过将goodFn函数return出去,现在goodFn可以在不让外部影响的情况下,只向外部提供加工后的a变量了。也就是私有化变量,外面的人可以用,但是没办法修改。

闭包的场景

其实在开发中闭包经常被使用

  1. 防抖
const debounce = function (func, wait) {
    let timeout
    return function () {
        let args = arguments
        if (timeout) clearTimeout(timeout)
        timeout = setTimeout(()=> {
            func.apply(this. args)
        }, wait)
    }
}
  1. 在react State Hook(useState)中
function Counter() {
  const [count, setCount] = useState(0);
  // ...
  return (
    <button onClick={() => setCount(count + 1)}>
      Increment
    </button>
  );
}

这个更新状态的函数setCount就是一个闭包,因为它能访问并修改其定义时所在作用域的状态变量,即使在组件重新渲染后,它仍然保持对原状态变量的引用。

  1. 在vue中
    1. 每个Vue组件都拥有独立的作用域,这意味着组件内部定义的方法和变量构成了一个闭包环境。组件的方法如 methods 和 computed 属性所包含的函数,它们都可以访问到组件自身的 data、props、computed 等属性,这种访问机制正是闭包的体现。
    1. Vue组件的生命周期钩子函数同样是闭包,它们能够在不同的生命周期阶段访问并操作组件的内部状态。

闭包的内存泄露


       借助于垃圾回收机制的标记清除法可以看出:全局变量printA不会被销毁那么变量a也不会被销毁,此时变量a存在内存泄露

闭包的this指向问题

  1. 普通函数调用
  • 如果闭包是在普通函数调用方式下形成的,那么闭包内部的this将指向全局对象(浏览器环境下是windo, 在严格模式下是undefined)
function outer() {
  var self = this;
  function inner() {
    console.log(this); // 在非严格模式下指向 window,在严格模式下为 undefined
  }
  inner(); // 此处直接调用,内部的 this 不指向 outer 函数
}
outer();
  1. 对象方法调用
  • 如果闭包是作为对象的方法被调用,则 this 指向该对象。
var obj = {
  outer: function() {
    var inner = function() {
      console.log(this); // 这里的 this 指向 obj
    };
    inner(); // 此处调用,内部的 this 指向 obj
  }
};
obj.outer();
  1. 构造函数调用
  • 若闭包在构造函数中定义并在 new 关键字创建实例时调用,this 指向新创建的实例。
function Outer() {
  this.inner = function() {
    console.log(this); // 这里的 this 指向 Outer 的实例
  };
}
new Outer().inner();
  1. 箭头函数
  • 箭头函数不创建自己的 this,它会捕获其所在(即定义的位置)上下文的 this 值。
function outer() {
    this.someValue = 'I am from outer';
    const inner = () => {
        console.log(this); // 这里的 this 指向 outer 函数调用时的 this { someValue: 'I am from outer', method: λ:outer }
    };
    inner(); // 若 outer 作为对象方法调用,此处的 this 也将指向该对象
}
const obj = {
    method: outer
};
obj.method(); // 这里的 this 指向 obj
  1. Function.prototype.bind()、call()、apply() 方法调用
  • 使用这些方法调用闭包时,可以显式地设置 this 的指向。
function outer() {
  var inner = function() {
    console.log(this);
  };
  var boundInner = inner.bind(someObject);
  boundInner(); // 这里的 this 指向 someObject
}

       在闭包中维持稳定的 this 指向,常常采用的方法是保存外部作用域的 this (如使用 var self = this; 或者箭头函数),或者使用 .bind() 方法等技术手段。

引用:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
https://www.bilibili.com/video/BV1ot4y1j7W2/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=311cbee331f47013f566b865fc68f492
http://8.134.168.35/article/detail/48

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学途路漫漫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值