思维导图
通过下面的思维导图,我们先对JavaScript的函数作用域、闭包一些基本的了解。
函数作用域
作用域决定了变量的可访问性,全局作用域,局部作用域(函数作用域)。
let和const具有块作用域,块级作用域包括在函数内部和在一个代码块内部。
作用域链
表示不同作用域里面,有多个同名变量,变量的优先次序。
例如在下面的例子中,a变量的作用域链是:局部变量a(banana)->全局变量a(apple),优先取到局部变量。
代码语言:javascript复制let a = 'apple';
function say(){
let a = 'banana';
console.log(a); // banana
}
say();
*var的声明提升
用var声明变量,会把变量声明提升到所有代码之前,类似示例2.
代码语言:javascript复制// 示例1
var a = 'apple';
function say(){
console.log(a); // undefined
var a = 'banana';
console.log(a); // banana
}
say();
上面的示例1等同于示例2。
代码语言:javascript复制// 示例1
var a = 'apple';
function say(){
var a;
console.log(a); // undefined
a = 'banana';
console.log(a); // banana
}
say();
闭包
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
词法环境
词法环境(Lexical Environment)是作用域内部(隐藏)的关联对象。
词法环境对象由两部分组成:
- 环境记录(Environment Record) —— 一个存储所有局部变量作为其属性(包括一些其他信息,例如
this
的值)的对象。 - 对 外部词法环境 的引用,与外部代码相关联。
函数的执行,可以分为创建词法环境的阶段和执行的阶段。
创建阶段
创建作用域链、变量对象、决定this。
执行阶段
变量赋值、函数引用等。
看下面这个例子
代码语言:javascript复制let name = 'apple'
function say(){
let name = 'banana';
console.log(name);
}
内部的语法环境,有name这个变量,外部的语法环境也有name这个变量。
代码访问变量时,先搜索内部的语法环境,如果逐层搜索外层的外部环境。