参透JavaScript —— 花十分钟搞懂作用域

前言

本篇文章主要讲解 JavaScript 中的作用域

作用域(Scope)的概念

很多编程语言都具有作用域的概念,它是计算机程序设计中的一个核心概念,定义了变量(或函数)的可访问范围

在书籍《你不知道的 Javascript》上卷中有这样一句话:作用域是根据名称查找变量的一套规则

所以通俗来讲,作用域在 JavaScript 里可以理解为:作用域是一套规则,可以决定一个变量或函数的有效访问范围

JavaScript 有哪些作用域

不同的编程语言可能有不同的作用域及规则,除去特殊的 eval 作用域,在 JavaScript 中主要有三种作用域:

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

全局作用域

全局作用域很好理解,在程序执行时就会被创建,特点是程序的任何地方都可以访问到,如果是浏览器环境,则挂载在 window 对象上

js 复制代码
var a = 'fifteen'
console.log(window.a); // fifteen

函数作用域

函数作用域是指函数内部的区域,特点是:在程序外部无法访问到函数内部的变量

js 复制代码
function foo() {
    var b = "fifteen"
    console.log(b);
}
foo() // fifteen

比如我们定义一个 foo 函数,在函数内部定义一个变量 b

此时在 window 对象上只会挂载一个 foo 函数,而不存在变量 b,在外部访问会报错:Uncaught ReferenceError: b is not defined

块级作用域

块级作用域,使用 {} 包裹的代码,并且使用 letconst 等关键字声明的变量,会形成块级作用域

也就是说,像常用的 ifforwhiletrycatch 等,都可以形成块级作用域

一个最小化的例子是,在 if 内定义的变量 a,无法在外部访问

js 复制代码
if(true){
    const a = "fifteen"
}
console.log(a); // Uncaught ReferenceError: a is not defined

而在此之前,通过 var 定义的变量,由于存在变量提升的问题,会污染外部变量

js 复制代码
if(true){
    var b = "fifteen"
}
console.log(b); // fifteen

《Javascript 高级程序设计》第四版和《你不知道的 Javascript》上卷在相关内容中,都举了一个 for 循环的例子

js 复制代码
for(var i = 0; i < 3; i++){
    console.log(i);
}
console.log(i); // 3

在这段循环中,我们用 var 定义了一个 i 变量,定义这个变量的初衷是让它在循环内部控制循环次数,但现在情况不是想象中的这样,i 现在污染了外部环境,是绑定在 window 对象上的全局变量

使用 ES6 推出的 let 定义,情况就不一样了

js 复制代码
for(let i = 0; i < 3; i++){
    console.log(i);
}

现在,i 只能在 for 循环内部使用,外部访问会报错:Uncaught ReferenceError: i is not defined

IIFE 立即执行函数表达式

这一节我想聊聊 IIFE,主要搞懂以下三个问题:

  1. IIFE 是什么?概念
  2. 如何定义一个 IIFE?
  3. IIFE 解决了什么问题,起什么作用

首先,IIFE 是一个缩写,它的全称英文是 Immediately Invoked Function Expression,翻译过来就是:立即调用的函数表达式

IIFE 的行为,可以理解为一个函数在定义后就会立即执行,表现形式通常用 () 包裹的匿名函数,尾部再接一个 (),触发函数的执行

比如下面这段代码:

js 复制代码
(function (){
    /** code */
})()

这样的操作带来了一些好处,借用 MDN 中介绍 IIFE 的内容:通过创建新的作用域来避免污染全局命名空间

也就是说,IIFE 可以解决变量(函数)污染的问题,形成一个独立的作用域,不会在 window 对象中挂载

它与块级作用域的作用很相似,在 ES6 之前,IIFE 常被用来生成独立的作用域

MDN - IIFE 中列出了 IIFE 的三点作用:

  • 通过创建新的作用域来避免污染全局命名空间。
  • 创建新的异步上下文以在非异步上下文中使用 await。
  • 使用复杂的逻辑计算值,例如将多个语句用作单个表达式。

总结

我们上面讲了三种作用域,分别是:全局作用域、函数作用域、块级作用域,可能你写了很多年的JS,但你没有注意到这些概念,深入学习 Javascript 时,这都是不必可少的基础知识

最后的话,聊了一下 IIFE ,它可以解决变量污染的问题,形成一个独立的作用域,在 ES6 块作用域之前比较常用

参考资料

参透JavaScript系列

本文已收录至《参透 JavaScript 系列》,全文地址:我的 GitHub 博客 | 掘金专栏

交流讨论

对文章内容有任何疑问、建议,或发现有错误,欢迎交流和指正

相关推荐
雲墨款哥几秒前
Vue3 图片放大镜组件优化实践:用 transform 替代 relative 定位 & watchThrottled 优化性能
前端·vue.js
爱编程的喵4 分钟前
React Hooks + 自定义Hooks 打造现代化 Todo 应用:记事本
前端·react.js
咔咔一顿操作6 分钟前
5、Vue中使用Cesium实现交互式折线绘制详解
前端·javascript·vue.js·3d
JosieBook18 分钟前
【web应用】若依框架中,使用Echarts导出报表为PDF文件
前端·pdf·echarts
袁煦丞1 小时前
Photopea云端修图不求人!cpolar内网穿透实验室第641个成功挑战
前端·程序员·远程工作
yk-ddm1 小时前
JavaScript实现文件下载完整方案
前端·javascript·html
万少1 小时前
04-自然壁纸实战教程-搭建基本工程
前端·harmonyos·客户端
karl_hg1 小时前
Element Plus 自定义(动态)表单组件
前端·vue.js·element
南岸月明1 小时前
从焦虑到专注:副业半年后我才明白的3件事
前端
晓13131 小时前
JavaScript加强篇——第八章 高效渲染与正则表达式
开发语言·前端·javascript