Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JS基础知识总结(四):作用域与闭包

JS基础知识总结(四):作用域与闭包

原创
作者头像
前端林子
修改于 2019-01-12 05:46:59
修改于 2019-01-12 05:46:59
2.7K00
代码可运行
举报
文章被收录于专栏:前端技术总结前端技术总结
运行总次数:0
代码可运行

本文是JS基础知识总结的第四篇文章,整个JS基础知识总结系列的文章可戳:

一.变量类型

  • 原始类型、复合类型、类型判断
  • 数据类型转换
  • 值类型和引用类型

二.浅拷贝与深拷贝

  • 浅拷贝的实现方式
  • 深拷贝的实现方式
  • 与赋值的区别

三.原型与原型链

  • 原型
  • 原型链的定义

四.作用域与闭包

  • 作用域与作用域链
  • 变量提升、函数提升
  • 闭包

1.作用域

回顾下jQuery源码中,代码是包在(function(){//代码})()当中。主要的目的是希望里面的所有变量,不会暴露到外面,以防止变量全局污染,这就是函数作用域。

在JS中,变量的作用域有两种:全局作用域和函数作用域。ES6中有了块级作用域的概念,使用let、const定义变量即可。

1.1全局作用域

(1)没有用var声明的变量(除去函数的参数)都具有全局作用域,成为全局变量;

(2)window的所有属性都具有全局作用域;

(3)最外层函数体外声明的变量也具有全局作用域

最外层的作用域,具有全局作用域的变量可以被任何函数访问。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var data = {name:"peter"};
            (function f1(){
              console.log('data',data);//{name:"peter"}
            })();

这样的坏处就是变量间很容易产生冲突。比如开发者A定义一个变量,开发者B又定义了一个同名变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var data = {name:"peter"};//A定义
//省去100行代码
var data = {name:"lily"};//B定义

这样变量就很容易冲突。

1.2函数作用域

在函数作用域中定义的变量,在函数外部是无法访问的,会报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function f1(){
    var data={name:"peter"}
}
console.log('data',data)//Uncaught ReferenceError: data is not defined

1.3ES6中的块级作用域

什么是块级作用域呢?

任何一个对花括号({})中的语句都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,称之为块级作用域。

let所声明的变量,只在使用let所在的代码块内有效,在代码块外调用let声明的变量则会报错:

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    var a={name:"peter"}
    let b={name:"lily"}
    console.log('inner a',a)//{name:"peter"}
    console.log('inner b',b)//{name:"lily"}
}
console.log('a',a)//a{name:"peter"}
console.log('b',b)//Uncaught ReferenceError: b is not defined

说明:

(1)不存在变量提升,使用let命令声明的变量,只能在声明后使用,语法上称为“暂时性死区”

(2)使用const声明的变量不能再次赋值。

2.变量提升、函数提升

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log('f1',f1);
function f1(){
    console.log('enter f1')
}
var f1 = 1;

结果:

从结果可以看出打印的f1是函数,不是undefined,也不是后面变量声明的1。这说明: 函数提升优先于变量提升,会优先处理函数声明,再处理变量声明。

若声明的变量和声明的函数重名,则变量的声明不会影响函数的声明。

小结:

(1)使用var声明的变量存在变量提升,即可在声明变量之前使用变量。使用let、const声明的变量不能在声明变量之前使用变量。

(2)函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升会把变量声明挪到作用域顶部。

3.作用域链

要得到一个变量的值,若当前作用域没有定义,就到父级作用域寻找。如果父级作用域中也没找到,就再向一层去寻找,直到找到全局作用域。这种一层一层的关系,就是作用域链。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var data = { name: "peter" };
            (function f1() {
                var a = 1;
                (function f2() {
                    var b = 2;
                    console.log('data', data)//{name:"peter"},data向上顺着作用域寻找
                    console.log('a', a)//a向上顺着作用域寻找
                    console.log('b', b)//b是当前作用域的变量
                    console.log('c', c)
                })();//立即执行f2
            })();//立即执行f1

4.闭包

闭包:有权访问另一个作用域中的变量的函数。

换个直观的说法,在函数A内部有个函数B,函数B可以访问函数A中的变量,函数B就是闭包:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
           function A(){
                var a = 1;
                B = function(){
                    console.log('a in function B',a)
                }
            }
            A();
            B();//执行B,结果是:1    

优点:

可以避免全局变量的污染

缺点:

参数和变量不会被垃圾回收机制回收,闭包会常驻内存,增大内存使用率,使用不当容易造成内存泄漏。

关于闭包有个经典问题,就是循环中使用闭包解决用var定义变量的问题,下面有两个示例:

示例1:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            for (var i = 1; i <= 8; i++) {
                setTimeout(function () {
                    console.log(i)//结果:每秒输出一个9,共输出8个9
                }, i * 1000)
            }    

结果:

控制台打印的结果并不是原本以为的,按秒输出1,2,3,...,7,8,。而是按秒输出9,9,9,...,9,9,共输出8个9。

原因是setTimeout()是一个异步函数,会先把循环执行完,所以i的值是9,然后再按1秒一个,共输出8个9。

类似的问题还有给下面的每个<li>加个点击事件,点击对应的<li>就弹出对应<li>中的内容,示例二:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
        </ul>            

js:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var aLi = document.getElementsByTagName('li');
            for(var i = 0;i<aLi.length;i++){
                aLi[i].addEventListener('click',function(){
                    alert(i+1)
                })
            }

其实执行后会发现,不管点击哪个<li>,弹出的都是7。

上述两个例子的结果都不符合预期,解决的方法是可以通过闭包来解决:

针对示例一的解决方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
         for (var i = 1; i <= 8; i++) {
                ; (function (j) {
                    setTimeout(function () {
                        console.log(j)//结果:按秒输出1,2,3,...,8
                    }, j * 1000)
                })(i)
            }

当然除了闭包的解决方法,最简单的解决方法是使用let替代var来定义i:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        for (let i = 1; i <= 8; i++) {
                setTimeout(function () {
                    console.log(i)//结果:按秒输出1,2,3,...,8
                }, i * 1000)
            }            

针对示例二的解放方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var aLi = document.getElementsByTagName('li');
            for (var i = 0; i < aLi.length; i++) {
                ; (function (j) {
                    aLi[j].addEventListener('click', function () {
                        alert(j + 1)
                    })
                })(i)
            }

闭包的基本知识可参考:

学习Javascript闭包(Closure)

MDN

5.小结

本文主要分别介绍了作用域、作用域链、变量提升和闭包的内容,作为JS基础知识总结的最后一篇。如有问题,欢迎指正。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【前端】JavaScript 的神奇世界:作用域、闭包和变量提升的深入探索
对于输入的数组 A 为 ['jojo', 'okko', '张三', '秃头', '帅小伙'],数组 B 为 ['你好', '小脑斧', '大西瓜', '长得帅'],最终输出为:
CSDN-Z
2025/06/02
820
【前端】JavaScript 的神奇世界:作用域、闭包和变量提升的深入探索
JavaScript进阶教程(5)-一文让你搞懂作用域链和闭包
在JS中变量可以分为局部变量和全局变量,对于变量不熟悉的可以看一下我这篇文章:搞懂JavaScript全局变量与局部变量,看这篇文章就够了 作用域就是变量的使用范围,分为局部作用域和全局作用域,局部变量的使用范围为局部作用域,全局变量的使用范围是全局作用域。在 ECMAScript 2015 引入let 关键字之前,js中没有块级作用域---即在JS中一对花括号({})中定义的变量,依然可以在花括号外面使用。
AlbertYang
2020/09/16
3660
JavaScript进阶教程(5)-一文让你搞懂作用域链和闭包
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
所以,JavaScript是ArkTS的基础,本文就来介绍一下JavaScript执行机制的一些核心概念。
陆业聪
2024/07/23
2700
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
深入理解作用域和闭包
JavaScript中的变量是松散类型的,没有规则定义它必须包含什么数据类型,它的值和数据类型在执行期间是可以改变的。
神奇的程序员
2022/04/10
6220
深入理解作用域和闭包
理解JavaScript的作用域
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。作用域嵌套的查询规则:
4O4
2022/04/25
7860
JavaScript中的作用域和作用域链
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。可能这两句话并不好理解,我们先来看个例子:
刘亦枫
2020/03/19
2.6K0
JavaScript中的作用域和作用域链
JavaScript——作用域和闭包
本文着重于对 JavaScript 中的作用域和闭包机制进行剖析和说明,闭包本质上也是作用域的一种类型,因为在 JavaScript 里非常重要,所以我们把它在标题里单独列出来。
Html5知典
2019/11/26
7850
重学JS-8-函数作用域、闭包
通过下面的思维导图,我们先对JavaScript的函数作用域、闭包一些基本的了解。
luciozhang
2023/04/22
4140
重学JS-8-函数作用域、闭包
apply/call/bind、作用域/闭包、this指向(普通,箭头,JS/Vue的this)
这里要跟this指向区分开。(这里说的作用域先简单理解为,只是变量的作用域,跟this没关系)
用户4396583
2024/10/08
1980
JS作用域和变量提升看这一篇就够了
作用域是JS中一个很基础但是很重要的概念,面试中也经常出现,本文会详细深入的讲解这个概念及其他相关的概念,包括声明提升,块级作用域,作用域链及作用域链延长等问题。
蒋鹏飞
2020/10/15
7800
大白话解释作用域和闭包是个啥
常见的变量作用域就是 静态作用域(词法作用域) 与 动态作用域 。词法作用域注重的是 write-time ,即 编程时的上下文 ,而 动态作用域 则注重的是 run-time ,即 运行时上下文 。词法作用域中我们需要知道一个函数 在什么地方被定义 ,而动态作用域中我们需要关心的是函数 在什么地方被调用
JS菌
2019/04/10
5520
大白话解释作用域和闭包是个啥
JS作用域与闭包
全局变量在函数外定义,HTML中全局变量是window对象,所有数据对象都属于window对象。
用户7353950
2022/06/23
2.1K0
JS作用域与闭包
js中的作用域
相信自从es6出来之后,你一定多少知道或者已经在项目中实践了部分的块级作用域,在函数或者类的内部命名变量已经在使用let了,但是你知道它真正的作用是什么吗?又是因为什么我们要用这个块级作用域,本文与你一起探讨块级作用域的场景以及所有细节问题。
RobinsonZhang
2018/08/28
3.6K0
深入理解Javacript从作用域作用域链开始
作用域是你的代码在运行时,某些特定部分中的变量,函数和对象的可访问性。换句话说,作用域决定了变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
coder_koala
2019/07/30
5860
深入理解Javacript从作用域作用域链开始
JS进阶-作用域
定义:局部作用域的意思就是,变量只能在它的代码块或者函数内部访问,而不能在外部访问,局部作用域的变量在函数或代码块执行完后会销毁,不会影响全局作用域变量。
吃猫的鱼Code
2025/02/11
3310
JS知识点梳理之作用域、作用域链、柯里化、闭包
作用域是指 js 变量使用时所存在的一个区域,分为全局作用域(window)和局部作用域(function、setTimeout...等都会产生局部作用域)。当局部作用域变量名与全局作用域变量名重复时,局部变量会覆盖全局变量。
hellocoder2029
2022/10/21
4960
js函数、作用域和闭包
2.1 用function命令声明函数 function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数,函数体放在大括号里面
bamboo
2019/01/29
1.5K0
js函数、作用域和闭包
深入理解JavaScript作用域
在上一篇文章 深入理解JavaScript 执行上下文 中提到 只有理解了执行上下文,才能更好地理解 JavaScript 语言本身,比如变量提升,作用域,闭包等,本篇文章就来说一下 JavaScript 的作用域。
木子星兮
2020/07/27
7710
深入理解JavaScript作用域
作用域及作用域链的解释说明
javascript中作用域是指变量与函数可访问的范围。作用域分为两类,一种是全局作用域,一种是局部作用域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义。局部变量是在函数体内声明而且只作用在函数体内部以及该函数体的子函数的变量。下面我们对全局作用域和局部作用域来做一个深入的理解。
OECOM
2020/07/02
1.3K0
作用域及作用域链的解释说明
【JS】重温基础:闭包
这里先要了解一个概念,词法作用域:它是静态的作用域,是书写变量和块作用域的作用域**。
用户1462769
2019/09/19
2.1K0
相关推荐
【前端】JavaScript 的神奇世界:作用域、闭包和变量提升的深入探索
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验