JavaScript作用域与闭包
JavaScript作用域问题
JavaScript 有个特性称为作用域。尽管对于很多开发者来说,作用域的概念不容易理解,但它确实是JavaScript这门语言中非常重要的一个概念。理解作用域能让你编写更优雅、错误更少的代码,并能帮助你获得更好的开发体验。
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名称冲突。
- 作用域为你的代码提供了一个安全层级 ,可以提升性能,跟踪 bug 并减少 bug。
JavaScript中的作用域
- 全局作用域
- 局部作用域(函数作用域)
- 块级作用域(try catch, 或者ES6的let、const关键字所创建)
var、let、const 关键字
var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问,存在变量提升。
let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改(如果const定义的常量被赋值为一个引用数据类型,这个常量的引用不能修改,但是通过这个常量可以修改这个引用数据类型本身)
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 关于变量提升的说明
// 在js中函数声明、var定义的变量声明都会被提升
console.log(a); // undefined
var a = 10;
// 上面这段代码在js编译器看来等价于
var a
console.log(a); // undefined
a = 10;
// 但是对于let 和 const定义的关键字来说, 编译器会直接报错!
console.log(a); // ReferenceError: a is not defined
let a = 10;
JavaScript闭包
简单的来说:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
下面我们来看一段代码,清晰地展示了闭包:
1 | function foo() { |
而,一般来说我们使用闭包主要来实现两种效果
- 第一、使用闭包可以在JavaScript中模拟块级作用域(在ES6 规范之前,js除了try catch之后全局作用域和函数作用域,使用闭包的形式用函数作用域来模拟块级作用域)
- 第二、闭包可以用于在对象中创建私有变量
对于目前大量使用ES6规范的情况下,使用闭包来模拟块级作用域的时候确实少了。但是在创建私有变量的情况中,暂时没有更完美的方法了。即使在ES6的情况下,私有变量也只是一个提案(在变量前加 #号)。
或者使用symbol类型的值来作为属性名,防止外部调用时一般的遍历方式访问,但是这种形式也可以使用 Object.getOwnPropertySymbols() 返回自身的Symol属性,或者Symbol.for(),获取具体的symbol类型的值来访问。
《你不知道的JavaScript (上卷)》
这本书中详细说明了作用域与闭包的问题,想要深入了解的话,可以去阅读。以下是第一部分的目录:
- 第1章 作用域是什么
- 第2章 词法作用域
- 第3章 函数作用域和块作用域
- 第4章 提升
- 第5章 作用域闭包