1)全局作用域和函数作用域
(1)全局作用域
- 最外层函数和最外层函数外面定义的变量拥有全局作用域
- 所有未定义直接赋值的变量自动声明为全局作用域
- 所有 window 对象的属性拥有全局作用域
- 全局作用域有很大的弊端,过多的全局作用域变量会污染全局命名空间,容易引起命名冲突。
(2)函数作用域
- 函数作用域声明在函数内部的变量,一般只有固定的代码片段可以访问到
- 作用域是分层的,内层作用域可以访问外层作用域,反之不行
2)块级作用域
- try catch 中的catch分句可以创建块作用域
- 使用 ES6 中新增的 let 和 const 指令可以声明块级作用域,块级作用域可以在函数中创建也可以在一个代码块中的创建(由{ }包裹的代码片段)
- let 和 const 声明的变量不会有变量提升,也不可以重复声明
- 在循环中比较适合绑定块级作用域,这样就可以把声明的计数器变量限制在循环内部。
3)作用域提升(变量和函数的所有 声明 都会被移动到作用域的最顶端)
- 只有声明本身会被提升,赋值或其他运行逻辑会被停留原地。
4)作用域闭包
闭包的定义:
- 一个普通的函数function,如果它可以访问外层作用域的自由变量,那么这个函数和周围环境就是一个闭包;
闭包原因:
- 外层函数调用后,外层函数的函数作用域(AO)对象无法释放,被内层函数引用着
闭包作用:
- 能够访问函数定义时存在的词法作用域
- 私有化变量,避免全局变量的污染
- 创建模块
如何使用?
- a. ⽤外层函数包裹要保护的变量和内层函数。
- b. 外层函数将内层函数返回到外部。
- c. 调⽤外层函数,获得内层函数的对象,保存在外部的变量中——形成了闭包。
function foo() {
var a = 2
function bar() {
console.log(a)
}
return bar
}
const baz = foo()
baz()
// 上述例子:看上去foo函数的整个作用域即将被销毁,
// 但bar()本身声明的位置拥有涵盖foo作用域的闭包,使得foo作用域可以一直存活以供bar随时访问,
// 因此,bar持有对foo函数的内部作用域的引用,这个就叫做闭包。
作用域链:
- 每个javascript函数都是Function 对象的实例,Function中有一个内置属性叫做[[Scope]](这个在控制台中的函数的prototype中可以看到)
- [[Scope]]存的就是一个作用链数组,这是一个对象数组,每一个对象保存的就是每一层作用域/每一个执行环境
- 当js引擎进行查找时,会在这个数组中按顺序查找,最后一个对象就是global,查找完global依然找不到就会抛出异常。