《趣说前端 - 002 》— 两种作用域模型介绍
《趣说前端》— 第二篇
前言
首先我们必须清楚作用域是一个独立的概念,和语言无关。
接下来介绍词法作用域和动态作用域的基本知识。
入题
作用域是什么?
一个变量(参数)在程序中起作用的范围区域,也就是说只有在这个区域这个变量(参数)才是可见的;这个范围区域就是小生理解的作用域。
作用域模型又分为两种—— “词法作用域 & 动态作用域”。
词法作用域模型下作用域链十分规矩,基本按规矩办事;
- 动态作用域模型下作用域链比较洒脱,总能给人小惊喜;
首先说明一点 JavaScript 采用的是词法作用域。
词法作用域
词法作用域,也称静态作用域。JavaScript 的作用域在代码的编译阶段就已确定。这是一种既定的规则。
我们用一个例子解释一下:
var name='小生方勤';function consoleName(){ console.log(name);}function test(){var name='方小勤'; consoleName();}test();
这段代码会输出什么结果?结果很明显输出的是 '小生方勤'。
因为使用的是词法作用域模型,所以在函数声明的时候作用域就已经确定了。
引擎会先在 consoleName() 函数创建的作用域中查找 name 变量,并没有找到;这时引擎就会到包含 consoleName() 作用域的上一级作用域查找(也就是全局作用域) name 变量;发现全局作用域正好有 name 变量;所以输出的是 '小生方勤'。
再通过 JavaScript 的执行过程解释
JS 的执行过程分为两个阶段:
编译阶段(由编译器完成)
- 执行阶段(由引擎完成)
编译阶段
任务是翻译成可执行代码(JS 的作用域在这个阶段确定):
词法分析
语法分析(生成 AST)
- 代码生成(转化 AST 成可执行代码)
执行阶段
任务是执行可执行代码(并创建执行上下文)。
那么在什么情况输出的是‘方小勤’呢?
动态作用域
动态作用域是在代码运行的时候确定的。也就是说动态作用域模型的作用域链是基于调用栈的。
如果这段代码是具有动态作用域的语言,那么输出的结果就是 - '方小勤'。
引擎会先在 consoleName() 函数创建的作用域中查找 name 变量,并没有找到;这时引擎就会到调用 consoleName() 函数的 test() 函数的作用域查找 name 变量;发现正好有 name 变量;所以输出的是 '方小勤'。
你是不是发现动态作用域的规则有点像 this 机制呢?
作用域链
在每个执行上下文的变量环境中,都包含了一个外部引用(outer),用来指向外部的执行上下文。
当一段代码使用了一个变量时,JavaScript 引擎首先会在“当前的执行上下文”中查找该变量,比如上面那段代码在查找 name 变量时,在当前的变量环境中没有查找到, JavaScript 引擎继续在 outer 所指向的执行上下文(全局执行上下文)中查找。
我们把这个查找的链条就称为作用域链。
总结一下
词法作用域:作用域在写代码的时候就确定,即声明时确定;
- 动态作用域:作用域在运行代码的时候确定,即调用时确定。
最后在声明一下 JavaScript 采用的是词法作用域。
留问题
最上方的例子应如何修改才能使程序输出 - '方小勤'。
参考
《JavaScript 高级程序设计》
- 《你不知道的 JavaScript》上
NOT THE END
该系列首发于同名公众号【小生方勤】,扫码关注即可订阅(可进交流群)。
更多相关文章
- 一网打尽 JavaScript 的作用域[每日前端夜话0x49]
- Python的容器有哪些?分别有什么作用?
- 正则表达式中模式修正符作用详解
- PHP作用域和文件夹操作示例
- Java Dao层的作用
- return关键字在php中的作用(含详解)
- 详解PHP死循环写法和作用
- 学习PHP死循环写法和作用
- PHP的中间件是什么?有什么作用?