首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
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进阶教程(5)-一文让你搞懂作用域链和闭包
在JS中变量可以分为局部变量和全局变量,对于变量不熟悉的可以看一下我这篇文章:搞懂JavaScript全局变量与局部变量,看这篇文章就够了 作用域就是变量的使用范围,分为局部作用域和全局作用域,局部变量的使用范围为局部作用域,全局变量的使用范围是全局作用域。在 ECMAScript 2015 引入let 关键字之前,js中没有块级作用域---即在JS中一对花括号({})中定义的变量,依然可以在花括号外面使用。
AlbertYang
2020/09/16
3740
JavaScript进阶教程(5)-一文让你搞懂作用域链和闭包
大白话解释作用域和闭包是个啥
常见的变量作用域就是 静态作用域(词法作用域) 与 动态作用域 。词法作用域注重的是 write-time ,即 编程时的上下文 ,而 动态作用域 则注重的是 run-time ,即 运行时上下文 。词法作用域中我们需要知道一个函数 在什么地方被定义 ,而动态作用域中我们需要关心的是函数 在什么地方被调用
JS菌
2019/04/10
5610
大白话解释作用域和闭包是个啥
JavaScript——作用域和闭包
本文着重于对 JavaScript 中的作用域和闭包机制进行剖析和说明,闭包本质上也是作用域的一种类型,因为在 JavaScript 里非常重要,所以我们把它在标题里单独列出来。
Html5知典
2019/11/26
8020
浅谈js的内存与闭包
主要结合了内存的概念讲了js的一些的很简单、但是又不小心就犯错的地方。 结论:js执行顺序,先定义,后执行,从上到下,就近原则。闭包可以让外部访问某函数内部变量,而且会导致内存泄漏。
lhyt
2022/09/21
5200
浅谈js的内存与闭包
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
所以,JavaScript是ArkTS的基础,本文就来介绍一下JavaScript执行机制的一些核心概念。
陆业聪
2024/07/23
3020
JavaScript执行机制:变量提升、作用域链、词法作用域、块级作用域、闭包和this
深入理解作用域和闭包
JavaScript中的变量是松散类型的,没有规则定义它必须包含什么数据类型,它的值和数据类型在执行期间是可以改变的。
神奇的程序员
2022/04/10
6330
深入理解作用域和闭包
重学JS基础-作用域链和闭包
JS有一个全局对象,window,在全局声明的变量都属于window的属性,未使用声明符声明的属性也是window的属性。
Jou
2022/08/10
6590
JS知识点梳理之作用域、作用域链、柯里化、闭包
作用域是指 js 变量使用时所存在的一个区域,分为全局作用域(window)和局部作用域(function、setTimeout...等都会产生局部作用域)。当局部作用域变量名与全局作用域变量名重复时,局部变量会覆盖全局变量。
hellocoder2029
2022/10/21
5120
重学JS-8-函数作用域、闭包
通过下面的思维导图,我们先对JavaScript的函数作用域、闭包一些基本的了解。
luciozhang
2023/04/22
4300
重学JS-8-函数作用域、闭包
JS作用域与闭包
全局变量在函数外定义,HTML中全局变量是window对象,所有数据对象都属于window对象。
用户7353950
2022/06/23
2.1K0
JS作用域与闭包
【前端】JavaScript 的神奇世界:作用域、闭包和变量提升的深入探索
对于输入的数组 A 为 ['jojo', 'okko', '张三', '秃头', '帅小伙'],数组 B 为 ['你好', '小脑斧', '大西瓜', '长得帅'],最终输出为:
CSDN-Z
2025/06/02
1010
【前端】JavaScript 的神奇世界:作用域、闭包和变量提升的深入探索
apply/call/bind、作用域/闭包、this指向(普通,箭头,JS/Vue的this)
这里要跟this指向区分开。(这里说的作用域先简单理解为,只是变量的作用域,跟this没关系)
用户4396583
2024/10/08
2150
js作用域链与预解析
在script标签下直接声明的变量或函数,都会在全局作用域下。可以在程序任何地方拿到
心念
2023/01/11
4950
js函数、作用域和闭包
2.1 用function命令声明函数 function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数,函数体放在大括号里面
bamboo
2019/01/29
1.5K0
js函数、作用域和闭包
JS作用域和变量提升看这一篇就够了
作用域是JS中一个很基础但是很重要的概念,面试中也经常出现,本文会详细深入的讲解这个概念及其他相关的概念,包括声明提升,块级作用域,作用域链及作用域链延长等问题。
蒋鹏飞
2020/10/15
8010
深入理解Javacript从作用域作用域链开始
作用域是你的代码在运行时,某些特定部分中的变量,函数和对象的可访问性。换句话说,作用域决定了变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
coder_koala
2019/07/30
6020
深入理解Javacript从作用域作用域链开始
作用域及作用域链的解释说明
javascript中作用域是指变量与函数可访问的范围。作用域分为两类,一种是全局作用域,一种是局部作用域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义。局部变量是在函数体内声明而且只作用在函数体内部以及该函数体的子函数的变量。下面我们对全局作用域和局部作用域来做一个深入的理解。
OECOM
2020/07/02
1.3K0
作用域及作用域链的解释说明
深入理解JavaScript作用域
在上一篇文章 深入理解JavaScript 执行上下文 中提到 只有理解了执行上下文,才能更好地理解 JavaScript 语言本身,比如变量提升,作用域,闭包等,本篇文章就来说一下 JavaScript 的作用域。
木子星兮
2020/07/27
7910
深入理解JavaScript作用域
前端基础-JavaScript作用域与JS代码的运行
变量的作用域是在定义时决定而不是执行时决定的,也就是说词法作用域取决于编译阶段,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
cwl_java
2020/03/26
1.4K0
了解 JS 作用域与作用域链
不在任何函数内声明的变量(函数内省略var的也算全局)称作全局变量(global scope)
书童小二
2018/09/03
2.3K0
相关推荐
JavaScript进阶教程(5)-一文让你搞懂作用域链和闭包
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档