重学JS-8-函数作用域、闭包

96 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

前端开发的第三年,突然发现,对于JS,我还有很多不懂的地方,趁着最近需求少,不如静下心来,从头把JS再学一遍,查漏补缺。

本系列以廖雪峰的《JavaScript教程》和《现代 JavaScript 教程》两个电子书作为线索,对其中需要进一步了解的知识,会阅读更多的文章,并作为扩展知识记录下来。

新手建议先阅读上面两个电子书,本系列更适合用来复习旧知识查漏补缺

思维导图

通过下面的思维导图,我们先对JavaScript的函数作用域、闭包一些基本的了解。

《重学JavaScript-闭包》思维导图.png

函数作用域

作用域决定了变量的可访问性,全局作用域,局部作用域(函数作用域)。

let和const具有块作用域,块级作用域包括在函数内部和在一个代码块内部。

作用域链

表示不同作用域里面,有多个同名变量,变量的优先次序。

例如在下面的例子中,a变量的作用域链是:局部变量a(banana)->全局变量a(apple),优先取到局部变量。

let a = 'apple';
function say(){
	let a = 'banana';
	console.log(a);  // banana
}
say();

*var的声明提升

用var声明变量,会把变量声明提升到所有代码之前,类似示例2.

// 示例1
var a = 'apple';
function say(){
	console.log(a);  // undefined
	var a = 'banana';
	console.log(a);  // banana
}
say();

上面的示例1等同于示例2。

// 示例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)**是作用域内部(隐藏)的关联对象。

词法环境对象由两部分组成:

  1. 环境记录(Environment Record) —— 一个存储所有局部变量作为其属性(包括一些其他信息,例如 this 的值)的对象。
  2. 外部词法环境 的引用,与外部代码相关联。

函数的执行,可以分为创建词法环境的阶段和执行的阶段。

创建阶段

创建作用域链、变量对象、决定this。

执行阶段

变量赋值、函数引用等。

看下面这个例子

let name = 'apple'
function say(){
	let name = 'banana';
	console.log(name);
}

内部的语法环境,有name这个变量,外部的语法环境也有name这个变量。

代码访问变量时,先搜索内部的语法环境,如果逐层搜索外层的外部环境。