javascript作用域(Scope)和闭包(Closure)

我慢慢发现了一些曾经在学习C#时不曾关注或了解的东西,在不断学习js的过程中开始理解了。最近在读的书《you don’t know js》,让我再理解了编译原理,状态等概念,当然也更加深入理解了作用域,闭包等,希望这篇文章对大家有帮助。

 

几乎所有编程语言都具备一个:存储值到变量中,并能够查询和修改变量的值,这种称为程序状态。联系到状态机的概念,又可以加深理解,程序好比就是状态机,输入确定,输出也确定。那么,这些变量到底存在哪里,程序又是怎么找到它们的?通过定义一些规则来解决这些问题,这些定义良好的规则称为:Scope(作用域)

编译原理

不管是编译型语言,还是解释型语言,都会有下面的过程:

1. Tokenizing/Lexing(分词、词法分析):将字符串var a=2;转换为var,a,=,2,;这个过程称为分词。空格是否被分词取决于该空格是否有意义。

2. Parsing(语法分析):对上一步的结果进行处理,形成一个AST(抽象语法树),var a=2的AST的根节点可能就是VariableDeclaration。

3. Code-Generation代码生成:对上一步的结果进行处理,转换为可执行的代码

 

程序里面三个角色

Engine:负责javascript程序的编译和执行

Compiler:负责解析(AST),生成可执行的代码

Scope:负责维护一个所有变量的查询列表,并且强制执行一个规则:当前代码如何访问这些变量

 

编译器编译var a = 2;分为两个步骤

 

词法作用域(Lexical Scope)

javascript使用词法作用域,意思是,作用域在进行词法分析的时候,根据已经写好的代码块就确定了作用域。作用域是可以嵌套的,因为代码也是可以嵌套的,变量或函数在哪个作用域里面定义,就归属于哪个Scope。例如:

但是有两个特别的:eval,with 它们会创建新的Scope,同时eval和with性能低下,不建议使用,因为eval里面的字符串在运行时,需要重新编译,创建新的Scope。而且js引擎的优化会失效。

 

Block和function作用域

javascript主要是基于function的作用域,但是有两个例外:①with   ②try catch  ③let(ES6)  ④const(ES6)常量

 

Hoisting

单词的意思是举起,提升。这里通过例子可以理解为声明变量或函数的语句会前置

①var的声明会提升到赋值之前,例如:

等效于:

 

②另外一种常见的声明就是定义函数:

如果在外面调用a变量

由于没能在当前Scope(也是Global Scope)里面找到a的定义,所以抛出了引用错误。

注:TypeError和ReferenceError的区别在于,前者表示在Scope里面存在变量,只是未知的类型(例如var a;如果在a赋值之前调用,未确定a的类型,则为undefined)。

③函数定义会被hoisting,但是函数表达式不会:

foo属于变量,会被后hoisting,但是bar不会,通过赋值表达式已经将引用赋给了foo,所以bar会引用错误。

④同时存在变量声明和函数声明,函数声明优先hoisting

⑤如果多个函数声明,后面的会覆盖前面的

⑥如果函数声明存在某些block里面,这种情况不同的浏览器会有不同的处理,可以忽略

 

Scope Closure(闭包)

闭包是贯穿整个javascript的,闭包存在是因为javascript的词法作用域,作用域的包含关系造就了闭包。例如:

其实这里能够更深的理解所谓的全局变量,全局变量不会被释放,原因在于全局变量存在于global scope里面,被我们定义的function作用域引用了,所以不会释放。

 

利用闭包的2种方式

①循环+闭包(保持变量的值)

循环结束才会运行所有的setTimeout,到那个时候i的值是6了,所以结果不是我们期望的。

 

闭包会产生一个新的作用域,所以继续改进:

 

结果仍然不是预期,为什么?因为我们产生了一个空的作用域,没有保存任何变量,i的取值最终仍然找到最外层的i=6,继续修改:

这意思是,为每个setTimeout维持了一个作用域,通过闭包的引用,保持了变量的值。实现了我们想要的效果。优化的代码入:

 

②模块+闭包

模块可以理解为一个具有独立功能,对外提供简单的接口使用而不用了解内部细节。实例代码:

定义个模块管理工具:

应用该模块管理工具定义模块:

注:ES6已经支持模块定义

 

本文参考原文:https://github.com/getify/You-Dont-Know-JS

javascript
作者:张雪飞
出处:https://zhangxuefei.site/p/1473
版权说明:欢迎转载,但必须注明出处,并在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注