说说你对闭包的理解?闭包使⽤场景

作用链域

JavaScript 的作用域链(Scope Chain)是指在代码中访问变量时的查找路径。

当 JavaScript 引擎在执行代码时,会根据作用域链来确定变量的可访问范围。

作用域链由多个执行上下文(Execution Context)组成,每个执行上下文都有一个关联的变量对象(Variable Object),变量对象保存了当前执行环境中定义的所有变量和函数。

当需要访问一个变量时,引擎会从当前执行上下文的变量对象开始查找,如果找不到,则沿着作用域链继续向上查找,直到全局执行上下文的变量对象。

如果在作用域链的任何级别上找不到该变量,则会抛出 ReferenceError。

作用域链的形成是由变量的作用域规则决定的

在 JavaScript 中,有三种类型的作用域:

  • 全局作用域、
  • 函数作用域
  • 块级作用域。

每个作用域都会创建一个新的执行上下文,并将其添加到当前的作用域链中。

作用域链是 JavaScript 用来管理变量访问权限的一种机制,它决定了变量在代码中的可见性和可访问性。

通过理解作用域链,我们能更好地理解 JavaScript 中的作用域和变量的生命周期。

JavaScript 的作用域链是指在代码中访问变量时的查找路径。我将通过一个例子来说明作用域链的概念。

更多详细内容,请微信搜索“前端爱好者戳我 查看

var globalVariable = 'Global'; // 全局变量

function outerFunction() {
    
    
  var outerVariable = 'Outer'; // 外部函数变量
  
  function innerFunction() {
    
    
    var innerVariable = 'Inner'; // 内部函数变量
    
    console.log(innerVariable); // 在内部函数中访问内部变量
    console.log(outerVariable); // 在内部函数中访问外部函数变量
    console.log(globalVariable); // 在内部函数中访问全局变量
  }
  
  innerFunction(); // 调用内部函数
}

outerFunction(); // 调用外部函数

在上面的例子中,我们定义了三个不同级别的变量:

  • 全局变量 globalVariable
  • 外部函数变量 outerVariable
  • 内部函数变量 innerVariable

当执行 outerFunction() 后,会创建一个包含 outerVariableinnerFunction 的函数执行上下文(即外部函数的执行上下文)。

然后,在 outerFunction() 中调用 innerFunction() 时,会再次创建一个包含 innerVariable 的函数执行上下文(即内部函数的执行上下文)。

当内部函数访问变量时,JavaScript 引擎会按照作用域链的顺序进行查找。

  • 首先,它会在当前执行上下文的变量对象中查找变量,即在内部函数的执行上下文中查找 innerVariable
    • 如果找到了该变量,则直接使用。
    • 如果没有找到,则会继续向上一级的执行上下文即外部函数的执行上下文中查找,即在外部函数的执行上下文中查找 outerVariable
    • 如果还是找不到,则继续向上一级的执行上下文即全局执行上下文中查找,即在全局执行上下文中查找 globalVariable

在这个例子中,当 innerFunction() 执行时,会按照作用域链的顺序找到并访问到 innerVariableouterVariableglobalVariable

如果在作用域链的任何级别上找不到变量,就会抛出 ReferenceError

这就是作用域链的工作原理,它决定了变量在代码中的可见性和可访问性。

闭包

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

闭包是指 有权访问另⼀个函数作用域中变量的函数,创建闭包的最常⻅的方式就是: 在⼀个函数内创建另⼀个函数, 通过另⼀个函数访问这个函数的局部变量,利用闭包可以突破作用链域

闭包的特性:

  • 函数内再嵌套函数
  • 内部函数可以引用外层的参数和变量
  • 参数和变量不会被垃圾回收机制回收

说说你对闭包的理解

  • 使用闭包主要是为了设计私有的方法和变量 。
    • 闭包的优点是可以避免全局变量的污染,
    • 缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

在js中, 函数即闭包, 只有函数才会产生作用域的概念

  • 闭包 的最大用处有两个,

    • ⼀个是可以读取函数内部的变量,
    • 另⼀个就是让这些变量始终保持在内存中
  • 闭包的另⼀个用处, 是封装对象的私有属性和私有方法

    • 好处:能够实现封装和缓存等;
    • 坏处:就是消耗内存 、不正当使用会造成内存溢出的问题

使用闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中, 内存消耗很大,所以不能滥用闭包, 否则会造成网页的性能问题,在IE中可能导致内存泄露
    • 解决方法是,在退出函数之前,将不使用的局部变量全部删除

总结

闭包的定义其实很简单: 函数A内部有⼀个函数B , 函数
就是闭包B 可以访问到函数 A 中的变量,那么函数B就是闭包

function A ( ) {
    
    
    let a = 1
    window .B = function ( ) {
    
    
        console . log( a)
    }
}
A ( )
B ( ) // 1

闭包存在的意义就是让我们可以间接访问函数内部的变量

扩展 循环中使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {
    
    
    setTimeout(function timer() {
    
    
        console.log(i)
    }, i * 1000)
}

首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕, 这时候 i 就是 6 了,所以会输出⼀堆 6

解决办法有三种

第⼀种是使用闭包的方式

for (var i = 1; i <= 5; i++) {
    
    
    ;(function(j) {
    
    
        setTimeout(function timer() {
    
    
            console.log(j)
        }, j * 1000)
    })(i)
}

在上述代码中, 我们首先使用了立即执行函数将 i 传入函数内部, 这个时候值就被固定在了参数 j 上面不会改变, 当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j ,从而达到目的

第⼆种就是使用 setTimeout 的第三个参数

这个参数会被当成 timer 函数的参数传入

for (var i = 1; i <= 5; i++) {
    
    
    setTimeout(
        function timer(j) {
    
    
            console.log(j)
        },
        i * 1000,
        i
    )
}

setTimeout 的第三个参数

在 JavaScript 中,setTimeout 函数是用于延迟执行一个函数或一段代码。它的语法如下:

setTimeout(callback, delay, arg1, arg2, ...);

其中:

  • callback 是要执行的函数或代码块。
  • delay 是延迟的时间(以毫秒为单位)。
  • arg1, arg2, … 是可选的参数,在延迟结束后会作为参数传递给回调函数。

现在来看第三个参数,它表示延迟结束后作为参数传递给回调函数的值。这个参数可以是任意类型的数据

让我们通过一个例子来说明第三个参数的使用:

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

setTimeout(greet, 2000, "John");

在上面的例子中,setTimeout 函数将延迟 2000 毫秒后执行 greet 函数,并且将字符串 "John" 作为参数传递给 greet 函数。当延迟结束后,greet 函数会被调用,并输出 "Hello, John!"

这里的 "John" 就是 setTimeout 的第三个参数,它会在延迟结束后作为参数传递给回调函数 greet

需要注意的是,setTimeout 的第三个参数是可选的,如果不提供,则回调函数不会接收任何参数。如果提供了多个参数,它们会按照顺序传递给回调函数。

第三种就是使用 let 定义 i 了来解决问题了

这个也是最为推荐的方式

for (let i = 1; i <= 5; i++) {
    
    
    setTimeout(function timer() {
    
    
        console.log(i)
    }, i * 1000)
}

おすすめ

転載: blog.csdn.net/BradenHan/article/details/135007119