深入了解js这门语言后,才发现它有着诸多众所周知的难点(例如:闭包、原型链、内存空间等)。有的是因为js的设计缺陷导致的,而有的则是js的优点。不管如何,总需要去学会它们,在学习过程中我觉得只看别人的文章并不能做到深刻理解,所以我决定写这一系列的文章来记录我所学习到的知识点,也方便自己以后回顾,如有写错的地方欢迎指正。 废话不多说,马上进入正题!
顺序执行
每个人刚学JS时都会知道JS执行代码时是顺序执行的,即从上到下执行。但看下面这段代码你就会发现事情并不是这么简单。
function foo() {
console.log(1)
}
foo() // 2
function foo() {
console.log(2)
}
foo() // 2
两次执行foo函数时打印的结果都是2。这是因为JS在执行代码时是一段一段地顺序执行,而不是一行一行地执行。即当JS拿到某一段代码时,它会事先地做一些准备工作,然后再一行一行地去执行。
准备工作
所谓的准备工作有两件事,我们分别通过代码来说明。
1、
console.log(f1) // function f1() {}
function f1() {} // 函数声明
上面的代码说明JS在准备工作阶段会提前声明函数,即把函数都声明并赋值好了再来执行代码。
2、
console.log(a) // a is not defined
console.log(a) // undefined
console.log(f1) // undefined
var a = 10
var f1 = function() {}
分析上面的代码,我们发现JS做的一种准备工作就是声明变量但不进行赋值操作。容易让人混淆的是函数表达式,它和函数声明不同,仅仅是像变量a一样被声明但未被赋值。
3、
JS在准备阶段还会做一件事情,就是给this赋值。至于this在不同环境下是什么值,可以参考之前的文章JavaScript难点系列(二):this。
执行上下文
执行上下文(也称执行上下文环境)就是在准备阶段产生的。在全局环境下,执行上下文有以下的数据:
- 未赋值的变量(包括函数表达式)
- 已赋值的this
- 已赋值的函数声明
而在函数作用域环境下,会多出参数、arguments变量、作用域链中的变量 这三种数据内容,而且它们都是被赋值的。函数每被调用一次就会产生一个新的执行上下文环境,因为不同的调用可能会产生不同的参数。
综上,用通俗的话给执行上下文下个定义就是:执行代码前把要用到的所有变量都先声明好,并且给其中的一些变量先赋值。
理解了执行上下文后,就出现一个新的问题:在执行代码时由于函数嵌套等原因会有很多次函数调用,从而产生了很多的执行上下文环境,那么这些执行上下文环境又应该如何管理?何时去销毁(或保存)?
执行上下文栈
针对上面提到的问题,JS会维护一个执行上下文栈(也称函数调用栈)来解决。栈底永远是全局上下文环境,而栈顶就是正在执行的上下文,因此在某个时间点只能有一个上下文环境在执行。
执行上下文栈工作的机制就是:每次函数调用(包括调用函数本身)都会产生执行上下文环境,并压入栈中;当函数调用完成时,这个上下文环境会出栈并销毁(不考虑闭包的情况)。
我们通过一段代码来具体地说明:
function foo() {
function bar() {
console.log(1)
}
bar()
}
foo()
首先是全局环境入栈:
global
然后调用foo函数,foo入栈
foo
global
foo函数中再调用bar函数,bar入栈
bar
foo
global
bar函数执行完毕后bar出栈
foo
global
foo函数执行完毕后foo出栈
global
最后关闭浏览器时全局环境出栈。
闭包
上面提到,函数在执行完毕后其执行上下文就会出栈且被销毁。但有一种情况是例外的,就是闭包。它能阻止它所包含的变量所在的那个执行上下文被销毁。这句话有点绕,需要多读几遍。引用JS高程里对闭包的定义:
闭包是指有权访问另一个函数作用域中的变量的函数
来看一个例子:
function foo() {
var a = 10
return function bar() {
console.log(a)
}
}
var fn1 = foo()
var a = 100
fn1() // 10
上面代码中当 var fn1 = foo() 执行完后,按照我们前面的分析,这时候应该销毁掉foo函数的执行上下文。但这里foo函数的执行上下文只是弹出执行上下文栈,并不会被销毁,会被保存在内存中。这样调用fn1函数(即bar函数)时,可以在内存中找到变量a的值
所以使用闭包会使类似foo执行上下文这类本应该被销毁的,却没有被销毁,从而增加了内存开销。
写在最后
我个人开了一个公众号“前端搬运小工”,我会定期推送优秀的前端精选文章,拒绝无脑基础入门的文章,带给你不一样的前端视角。