携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
大家好!我是前端爬楼工程师🚹,一个野生程序员。好奇新技术,仰慕牛大佬。如果喜欢我的文章,可以关注➕点赞,为我注入能量,与我一同成长吧~
Javascript能够在浏览器端运行,离不开三样东西,分别是:v8引擎,编译器,作用域。
一切由var a = 1开始:
编译器
首先是编译器工作:
- 将以上代码拆分
vara=1四个部分,这个步骤叫分词,也叫词法分析; - 根据分词解析成AST,其结构类似与JS对象
{声明:{变量:{赋值:{值:}}}},这个步骤叫解析或者语法分析; - 最后根据AST生成可执行的字节码,由引擎运行起来, 这个步骤叫代码生成;
作用域
编译完毕之后,就是引擎来执行代码了,执行代码的过程需要作用域来辅助。
在此之前,我们了解一下作用域的范围,作用域范围如图所示,是嵌套的。
- 全局作用域有
foo foo作用域下有a和barbar作用域下游b
执行var a,首先引擎会通过作用域使用LHS寻找a这个变量,因为变量是用var修饰的,所以就会在当前作用域创建这个变量a,如果没有修饰则会挂在window下。(严格模式会创建失败并抛出ReferenceError)
接着执行a = 1, 这个时候作用域会使用RHS查询a这个变量,它的作法和LHS不一样,它如果找不到直接抛出ReferenceError,并不会创建变量,如果找到了变量,将1赋值给a;如果此时有代码a(),这时会抛出TypeError。
闭包
通常呢,函数在执行完毕之后就会被垃圾回收,如下所示:
function getList(){
// code ...
}
getList()
但是如果是这样呢:
function tools(){
var transfer = function(){}
var isNull = function(){}
return {
transfer: transfer,
isNull: isNull
}
}
var myTools = tools()
myTools.transfer()
myTools.isNull()
我们顺着上面的思维,tools执行完毕,是要被垃圾回收的,然而它的返回值被myTools接收, 相当于tools的作用域被myTools保护起来了,我们称之为闭包,myTools可以在其作用域范围内的任何位置都可以使用其内部作用域的属性。
类似的回调函数也是一种闭包:
function foo(cb){
cb()
}
var a = 0
function bar(){
console.log(a)
}
foo(bar)
这里可以总结一下: 当函数可以记住并能访问所在词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。