js--作用域、闭包

本文深入探讨JavaScript中的作用域概念,包括函数作用域、块级作用域以及在for循环中的应用。通过示例解释了let、const和var声明的变量在不同作用域内的行为,并展示了闭包如何允许子函数访问外部作用域的变量,同时提醒了闭包可能导致的内存泄漏问题。此外,还讨论了在异步操作中如何正确处理作用域,确保预期的行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用域

函数作用域

  • 一个函数执行多次后不是只会产生一个变量,而是产生不同地址的变量,变量之间的数据不会共享
  • 函数每次调用都会创建一个新作用域
       let q = 1

        function a() {
            let n = 1

            function a_b() {
                let m = 1

                function a_b_c() {
                    let l = 1
                    console.log('n--' + ++n);
                    console.log('m--' + ++m);
                    console.log('l--' + ++l);
                    console.log('q--' + ++q);
                }
                a_b_c()
            }
            a_b()
        }
        a() //n--2 m--2 l--2 q--2
        a() //n--2 m--2 l--2 q--3
  • 如果子函数被使用时父级环境将被保留,它本身的环境不被保留
        let q = 1
        function a() {
            let n = 1

            return function a_b() {
                let m = 1

                return function a_b_c() {
                    let l = 1
                    console.log('n--' + ++n);
                    console.log('m--' + ++m);
                    console.log('l--' + ++l);
                    console.log('q--' + ++q);
                }

            }

        }
        let site = a() //n--2 m--2 l--2 q--2
        console.log(site);
        //site= ƒ a_b() {
        //           let m = 1

        // return function a_b_c() {
        //     let l = 1
        //     console.log('n--' + ++n);
        //     console.log('m--' + ++m);
        // …
         site()() //n,m,l,q=2
         site()() //n,q=3  m,l=2  原因是 a_b()被外部的变量引用了,所以a的环境没有消失
let q = 1

        function a() {
            let n = 1

            return function a_b() {
                let m = 1

                return function a_b_c() {
                    let l = 1
                    console.log('n--' + ++n);
                    console.log('m--' + ++m);
                    console.log('l--' + ++l);
                    console.log('q--' + ++q);
                }
            }
        }
        let siteb = a()() //引用a_b_c
        siteb() //n,m,l,q=2
        siteb() n,m,q=3  l=2  原因是a_b_c()被引用了,所以他的父级及父级以上的环境就被保留了

构造函数中的作用域

构造函数中的方法 (例如 fn())相当于普通函数中 return 了一个fn(),所以fn也是被引用了,所以他的父级的环境被保留了

 function con() {
            let a = 1
            this.b = 1
            this.fn = function() {
                console.log(++a);
                console.log(++this.b);
            }
        }
        let n = new con()
        console.log(n); // con {b: 1, fn: ƒ}
        console.log(n.a); //undefine  但如果是构造函数中 this.a 等于1,就会有值
        console.log(n.b) //1
        n.fn() //a=2
        n.fn() //a=3
        console.log(n.b) //3

let、const、var的作用域

在{}里面就形成了一个作用域,let、const是块级作用域,所以在作用域外部是访问不到他们声明的变量的,var没有块级作用域,它声明的变量在全局作用域里,会被挂载到window对象上,所以可以访问到

		{
            let aa = 1
        }
        console.log(window.aa); //undefined
        // console.log(aa); //报错
        {
            var bb = 2
        }
        console.log(window.bb); //2
        console.log(bb); //2

但是在函数作用域里用 var 和 let/const声明的变量,外部的作用域都访问不到

function yaild() {
            let cc = 3
            var dd = 4
        }
        console.log(cc); //报错
        console.log(dd); //报错

for循环里的作用域(for , for in ,for of ,for each)

for循环也会是一个作用域,和{}一样,在里面声明的var会被挂载到window上面,let则不会

  for (var i = 0; i < 4; i++) {
            console.log(i);
        }
        console.log('全局' + i);//4

 for (let i = 0; i < 4; i++) {
            console.log(i);
        }
        console.log('全局' + i);//报错

for循环里面的异步操作(定时器,在for循环里面为dom对象添加事件等)

这里用let 声明的i 有块级作用域,for循环了四次就是生成了4个块级作用域,每个块级作用域里的i分别为 0 1 2 3,每个定时器里面的i也是用的每个作用域里的i,所以能正常打印出 0 1 2 3

  for (let i = 0; i < 4; i++) {
            // console.log(i);
            setTimeout(() => {
                console.log(i); //0 1 2 3
            }, 1000)
        }
        console.log('全局' + i); //报错

但是用var 声明的i是被挂载到全局作用域上的,定时器里的i用的都是window.i,所以当定时器执行里面的函数的时候,for循环已经结束,i==4了,所以打印出来的结果是4 4 4 4

 for (var i = 0; i < 4; i++) {
            // console.log(i);
            setTimeout(() => {
                console.log(i); //4 4 4 4
            }, 1000)
        }
        //这个全局4会被最先打印出来
        console.log('全局' + i); //4

如何用var的时候也要达到正常输出的效果呢?可以在定时器外面在套一层函数,也就是将定时器放入一个函数作用域里面,因为函数里面的变量都不会被挂载到全局上

for (var i = 0; i < 4; i++) {
            (function fn(a) {
                setTimeout(() => {
                    console.log(a); //0 1 2 3 
                }, 1000)
            })(i) //这里将for循环里的i赋值给fn()里面的a,也就是a 来保存i的值
            // console.log(i);

        }
        //这个全局4会被最先打印出来
        console.log('全局' + i); //4

闭包

闭包指子函数可以访问外部作用域变量的函数特性,即使在子函数作用域外也可以访问。就是子函数可以访问父函数的变量,如果没有闭包那么在处理事件绑定,异步请求时都会变得困难。
根据作用域的特性,如果子函数引用了父函数的变量,那么父函数的作用域也不会消失,所以可能会造成内存泄漏的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值