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

交流讨论

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

相关推荐
__zRainy__15 分钟前
Node.js Web 框架选型指南:从 Express 到 Hono 的全景对比
前端·node.js·express·koa·nestjs·egg·fastify
西瓜有点饿24 分钟前
PostCSS 和 UnoCSS 的作用和区别
前端·postcss
mCell25 分钟前
Lua 编程入门:从基础语法到元表
javascript·算法·lua
伟大的兔神1 小时前
我做了一个本地优先的 AI 图片工作台:Loomora v1.0.0 正式发布
前端·javascript·vue.js
90后的晨仔2 小时前
uni-app项目 Vue3 状态管理 Pinia 完全指南:从概念到实战的深度解析
前端
90后的晨仔2 小时前
uni-app 在 iOS 平台跳转页面时移除底部安全区域的完整技术指南
前端
用户938515635073 小时前
React + JWT 登录鉴权底层原理与工程化实践
前端·react.js
zww89491113 小时前
家政派单系统开发实战:架构设计与派单算法指南
前端·系统架构
fangzhanpeng1683 小时前
(前端)2.js变量作用域样例
开发语言·前端·javascript
赵广陆3 小时前
企业实战:web服务集成
前端·pycharm·fastapi