参透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 博客 | 掘金专栏

交流讨论

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

相关推荐
IT_陈寒15 分钟前
SpringBoot性能翻倍秘籍:从自动配置到JVM调优的7个实战技巧
前端·人工智能·后端
How_doyou_do23 分钟前
JS之刷刷
开发语言·javascript·ecmascript
叮咚前端27 分钟前
vue3笔记
前端·javascript·笔记
薛定谔的算法43 分钟前
面试官问箭头函数和普通函数的区别?这才是面试官最想听到的
前端·javascript·面试
pepedd86444 分钟前
AI Coding 最佳实践-从零到一全栈项目编写
前端·aigc·trae
砂糖橘加盐44 分钟前
非 AI 时代前端是如何设计一个组件的
前端·javascript·vue.js
艾小码1 小时前
告别JavaScript类型转换的坑:从隐式陷阱到显式安全指南
前端·javascript
LEAFF1 小时前
性能优化工具Lighthouse操作指南
前端
Cache技术分享1 小时前
176. Java 注释 - 类型注释和可插入类型系统
前端·后端
粥里有勺糖1 小时前
视野修炼-技术周刊第125期 | nano-banana
前端·github·aigc