工作处于实习阶段, 趁着现在还有点空闲时间, 又忍不住掏出床头的JavaScript 高级程序设计, 重新温习了一遍.
在串讲作用域和作用域链之前, 先梳理清楚几个重要概念.
执行环境
执行环境定义了变量或函数有权访问的其他数据, 决定了他们各自的行为. 每个执行环境都有与其相关的变量对象. 在 web 浏览器中, 全局的执行环境是 window, 因此全局变量和函数都是作为 window 对象的属性和方法创建的.
变量对象
环境中定义的变量和函数都保存在变量对象中, 解析器在处理数据的时候会在后台使用它.(注: 在函数的执行环境中, 变量对象称为活动对象)
环境栈
每个函数都有自己的执行环境, 当执行流进入一个函数时, 函数的执行环境被压入环境栈中, 当函数执行完毕, 函数从执行栈中弹出, 将控制权返回给之前的执行环境.
弄清楚了以上概念, 看代码示例.
- var leader = "shuDianZhang";
- function employeeHouse() {
- var hostName = "liHua"; // 主人名
- var furniture = "sofa"; // 家具
- function getFurniture() {
- return furniture;
- }
- getFurniture();
- }
- employeeHouse();
执行该代码, 首先压入栈底的是全局执行环境 window, 全局执行环境中的变量对象上有变量 leader 和函数 employeeHouse(). 当执行 employeeHouse(), 执行流进入函数内部的时候, employeeHouse()的执行环境压入环境栈. 代码继续执行, 当执行 getFurniture()函数的时候, 其执行环境继续压入环境栈. 整个流程大致如下图所示.
环境栈变化图. png
当 getFurniture()函数执行结束之后, getFurniture()函数的执行环境从环境栈中弹出, 紧接着, employeeHouse()函数的执行环境从环境栈中弹出, 当我们关闭网页或者浏览器时, 全局执行环境自动销毁.
作用域
在 JavaScript 中是没有块级作用域的. 作用域只有全局作用域和局部作用域 (函数的执行环境) 之分. 也许是为了弥补 JavaScript 这一设计缺陷, 在 ES6 中引入了 let.
作用域链
在了解作用域链之前, 我们不妨设想作用域链的用途是什么? 如果没有作用域链, 结果会是怎样? 如果没有作用链, 就相当于缺少了一套访问变量和函数的规则, 没有了这套规则的约束, 就不能明确变量和函数的作用范围, 说白了就会乱套. 因此, 作用域链的用途, 是保证对执行环境有权访问的所有变量和函数的有序访问. 作用域链的制定的规则就是内部环境可以通过作用域链访问所有外部环境, 但是外部环境不能访问内部环境中的任何变量和函数. 作用域链的搜所过程是: 从作用域链的前端开始, 向上逐级查询与给定名字匹配的标识符. 如果找到了, 搜索过程停止, 否则继续沿着作用域链向上搜索, 一直追溯到全局执行环境中的变量对象为止.
当代码在一个环境中执行时, 会创建变量对象的一个作用域链. 作用域链的前端始终是当前执行的代码所在环境的变量对象. 作用域链的末端始终是全局执行环境的变量对象.
好了好了, 重点来了. 为了方便记忆, 我们把作用域链看作是一串烧烤.(夜晚写这个, 肚子有点饿了)假定这串烧烤是羊肉串, 那么穿在这竹签上的羊肉便是变量对象.
哆啦 A 梦喜欢铜锣烧这次将就强行烧烤. png
以最开始的代码段为例, 当代码执行 getFurniture()函数, 执行流进入 getFurniture()函数内部的时候, 可以知道, 当前执行的代码所在环境是 getFurniture()的执行环境. 这时候, 可以想象服务员递给你一串 "羊肉串", 羊肉串的最顶端就是 getFurniture()执行环境的变量对象.
好了, 作用域和作用域链的介绍先告一段落, 后续会有补充, 喜欢的朋友点个赞吧~ 一起学习, 一起进步!
来源: http://www.jianshu.com/p/146f95cfb6b9