0

0

javascript作用域底层作用分析

php中世界最好的语言

php中世界最好的语言

发布时间:2018-05-24 15:18:16

|

1374人浏览过

|

来源于php中文网

原创

这次给大家带来javascript作用域底层作用分析,javascript作用域底层使用的注意事项有哪些,下面就是实战案例,一起来看一下。

标签: javascript


什么是作用域(Scope)?

作用域产生于程序源代码中定义变量的区域,在程序编码阶段就确定了。javascript 中分为全局作用域(Global context: window/global )和局部作用域(Local Scope ,  又称为函数作用域 Function context)。简单讲作用域就是当前函数的生成环境或者上下文(注意:暂且不要与后面讲的执行上下文混淆了),包含了当前函数内定义的变量以及对外层作用域的引用

javascript 中的作用域:

作用域(Scope) -
window/global Scope 全局作用域
function Scope 函数作用域
Block Scope 块作用域(ES6)
eval Scope eval作用域

作用域定义了一套规则,这套规则定义了引擎如何在当前作用域以及嵌套作用域根据标识符来查询变量。反过来说N个作用域组成的作用域链决定了函数作用域内标识符查找到的值。

所以我们可以总结为:作用域(Scope)确定了当前上下文内定义的变量的可见性,即更下一层作用域可以访问到。并且作用域链(Scope Chain)也确定了在当前上下文中如何查找标识符的值。

图片描述

Scope分为Lexical Scope和Dynamic Scope。Lexical Scope正如字面意思,即词法阶段定义的Scope。换种说法,作用域是根据源代码中变量和块的位置,在词法分析器(lexer)处理源代码时设置。javascript 采用的就是词法作用域。

变量的访问规则:

  • 如果变量 a 在函数内部定义, 则函数内部其他变量具有访问变量 a 的权限,但是函数外部代码没有访问变量 a 的权限。所以同一作用域内变量可以相互访问,即 a、b、c 在同一个作用域他们就可以相互访问。这就像鸡妈妈有宝宝,鸡宝宝可以相互打闹,其他鸡就不能跟他们打闹了,为什么? 因为鸡妈妈不容许~ o(^∀^)o 。

let a = 1
function foo () {
    let b = 1 + a
    let c = 2
    console.log(b) // 2
}
console.log(c) // error 全局作用无法访问到 c
foo()
  • 如果变量 a 在全局作用域下定义(window/global),则全局作用域下的局部作用域内的执行代码或者说是表达式都可以访问到变量 a 的值。局部变量里的同名变量(a)会截断对全局变量 a 的访问。(这里的变量 a 就相当于是饲养员,候饲养员会在合适的时候给鸡儿们投食。但是农场主为了节约成本,规定饲养员要就近给鸡投食,当饲养员1离鸡宝宝更近时其他饲养员就不能千里迢迢跨过鸭绿江去喂鸡了。)

let a = 1
let b = 2
function foo () {
    let b = 3
    function too () {
        console.log(a) // 1
        console.log(b) // 3
    }
    too()
}
foo()

再次强调 javascript 作用域会严格限制变量的可访问范围: 即根据源代码中代码和块的位置,被嵌套作用域拥有对嵌套作用域的访问权限。(这一条规则说明整个农场是有规则的,不能反向的投食。)

作用域链 Scope Chain

作用域链,是由当前环境与上层环境的一系列作用域共同组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

上面解释的稍微有些晦涩,对于我这样大脑不好使的就需要在大脑里重复的'读'几次才能明白。那么作用域链是干嘛的? 简单的说作用域链就是解析标识符的,负责返回表达式执行时所依赖变量的值。再简单点回答:作用域链就是用来查找变量的,作用域链是由一系列作用域串联起来的。

在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是当前执行函数的作用域开始(下图中从左向右),查找同名的标识符,如果找到了就返回这个标识符对应的值,如果没找到继续搜索作用域链中的下一个作用域,如果搜索完所有作用域都未找到,则认为该标识符未定义。函数执行过程中,每个标识符值得解析都要经历这样的搜索过程。

图片描述

为了具象化分析问题,我们可以假设作用域链是一个数组(Scope Array),数组成员有一系列变量对象组成。我们可以在数组这个单向通道中,也就是上图模拟从左向右查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。直到最顶层(全局作用域),并且一旦找到,即停止查找。所以内层的变量可以屏蔽外层的同名变量。想想一下如果变量不是按从内向外的查找,那整个语言设计会变得N复杂了(我们需要设计一套复杂的鸡宝宝找食物的规则)

还是上面的栗子:

let a = 1
let b = 2
function foo () {
    let b = 3
    function too () {
        console.log(a) // 1
        console.log(b) // 3
    }
    too()
}
foo()

作用域嵌套结构是这样的:

图片描述

栗子中,当 javascript 引擎执行到函数 too 时, 全局、函数 foo、函数 too 的上下文分别会被创建。上下文内包含它们各自的变量对象和作用域链(注意: 作用域链包含可访问到的上层作用域的变量对象,在上下文创建阶段根据作用域规则被收集起来形成一个可访问链),我们设定他们的变量对象分别为VO(global),VO(foo), VO(too)。而 too 的作用域链,则同时包含了这三个变量对象,所以 too 的执行上下文可如下表示:

too = {
    VO: {...},  // 变量对象
    scopeChain: [VO(too), VO(foo), VO(global)], // 作用域链
}

我们可以直接用scopeChain来表示作用域链数组,数组的第一项scopeChain[0]为作用域链的最前端(当前函数的变量对象),而数组的最后一项,为作用域链的最末端(全局变量对象 window )。所有作用域链的最末端都为全局变量对象。

再举个栗子:

let a = 1
function foo() {
    console.log(a)
}
function too() {
    let a = 2
    foo()
}
too() // 1

这个栗子如果对作用域的特点理解不透彻很容易以为输出是2。但其实最终输出的是 1。 foo() 在执行的时候先在当前作用域内查找变量 a 。然后根据函数定义时的作用域关系会在当前作用域的上层作用域里查找变量标识符 a,所以最后查到的是全局作用域的 a 而不是 foo函数里面的 a 。

变量对象、执行上下文会在后面介绍。

闭包

在 JavaScript 中,函数和函数声明时的词法作用域形成闭包。我们来看个闭包的例子

let a = 1
function foo() {
  let a = 2
  function too() {
    console.log(a)
  }
  return too
}
foo()() // 2

这是一个闭包的栗子,一个函数执行后返回另一个可执行函数,被返回的函数保留有对它定义时外层函数作用域的访问权。foo()() 调用时依次执行了 foo、too 函数。too 虽然是在全局作用域里执行的,但是too定义在 foo 作用域里面,根据作用域链规则取最近的嵌套作用域的属性 a = 2。

再拿农场的故事做比如。农场主发现还有一种方法会更节约成本,就是让每个鸡妈妈作为家庭成员的‘饲养员’, 从而改变了之前的‘饲养结构’。

关于闭包会在后面的章节里也会有介绍。

从作用域链的结构可以发现,javascript引擎在查找变量标识符时是依据作用域链依次向上查找的。当标识符所在的作用域位于作用域链的更深的位置,读写的时候相对就慢一些。所以在编写代码的时候应尽量少使用全局代码,尽可能的将全局的变量缓存在局部作用域中。

不加强记忆很容记错作用域与后面将要介绍的执行上下文的区别。代码的执行过程分为编译阶段和解释执行阶段。始终应该记住javascript作用域在源代码的编码阶段就确定了,而作用域链是在编译阶段被收集到执行上下文的变量对象里的。所以作用域、作用域链都是在当前运行环境内代码执行前就确定了。这里暂且不过多的展开执行上下文的概念,可以关注后续文章。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

PromiseA+的实现步骤详解

EasyCanvas绘图库在Pixeler项目开发中使用实战总结

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

46

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

178

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

51

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

227

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

532

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

171

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号