原文: 【你不知道的JavaScript上卷】——作用域与闭包
JS语言万变不离其宗,其中最常用、最重要的也就是常用的几个大概念。数据类型、作用域、原型链、闭包、this指针、异步,不同的人理解不一样,不一样的书讲解的也不一样。但这本《你不知道的JavaScript》系列言简意赅,直指本质,值得反复阅读,每次阅读都感觉代码的设计实现精妙之处
一、浏览器如何运行代码?
我们每天写那么多代码来实现各种特效和自动化工程,难道不想知道每行代码究竟是怎样被编译和执行的吗?
简单的例子:var a = 2;
变量的赋值操作执行两个动作:
1、编译器会在当前作用域中声明一个变量(如果之前未声明过)
2、运行时引擎会在作用域中查找该变量,如果查找到就对其赋值
作用域链查找原则:
1、引擎从当前的执行作用域开始查找变量,如果查找不到,就会向上一级继续查找
2、当查找到最外层的全局作用域时,仍未找到则会停止查找
异常判断:
1、如果RHS查询在所有作用域中无法找到变量,引擎就会抛出 ReferenceError 异常
2、如果LHS查询在所有作用域中无法找到变量,在非严格模式下会创造一个具有该名称的变量
3、如果RHS查询查找到变量,但是对其进行不合理的操作,如对非函数类型调用、查找undefined\null 类型的属性,则会抛出 TypeError
编译过程对变量声明的处理:
1、包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理
2、函数声明能够被提升,函数表达式不能被提升
3、函数声明首先被提升,然后是变量提升
二、作用域
作用域:
负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前的代码对这些标识符的访问权限。
规则:
引擎沿着作用域链进行查找标识符
注意:
作用域分两种,一种为在词法阶段即确定的作用域,即词法作用域;另一种为在代码运行时确定的作用域,即动态作用域。
三、闭包
闭包是个很不好理解的概念,但用处很大。我一般通常理解为一个包装函数中嵌套了函数,函数一直保存着对包装函数作用域的引用即产生了闭包
闭包:
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前作用域之外执行。
闭包的作用很多,尤其是在ES5时代原生语言没有模块,通常都用闭包来营造模块化,以保持变量的私有化。
面试常见坑:
1、全局作用域 + 闭包
2、IIFE模式封闭for循环
3、块级作用域 + 闭包
模块要点:
1、必须有外部的封闭函数,该函数至少被调用一次(每次调用都会创建一个新的模块实例)
2、封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态
模块例子:
1、单例模式
2、多例模式