函数与作用域
由于作用域相关的东西太多,所以拆分成多个章节
一、作用域概述
二、标识符与作用域
三、全局和模块作用域
四、函数作用域
五、块作用域
六、作用域 与 this
1.函数的声明与作用域
这个标题有点奇怪,函数的声明貌似和作用域没有关系,当然它也确实和函数本身的作用域没有关系,但是大家都知道,js是词法作用域,也就是说,
函数在哪里声明的,那么它的outerEnv(外部或者说父级作用域)就是在那里,所以说函数的声明和其outerEnv有关系,那他是怎么实现的呢?
首先,我们想想,函数是什么?在执行函数声明的时候,js引擎会把它转为为含有一些特殊字段的对象,所以这里划重点:函数是一个对象
注:特殊字段包含:constructor, call, sourceCode等等
既然函数是一个对象,那么可以设置值,js在执行的时候,就会把当前的作用域做为其outerEnv,那么无论这个函数在哪里引用,他的outerEnv都是固定的,
在其声明的时候就创建好了,也就实现了我们所说的词法作用域
2..执行无默认值的函数流程
首先,我们来看看当函数执行的时候,代码执行一般过程:
1.执行函数会创建执行上下文context,而根据es规范中对context的字段说明,其包含两个env,分别是varEnv,和lexicalEnv,也就是变量环境(作用域)和词法环境
2.创建一个函数的作用域,并且varEnv和lexicalEnv都指向它
3.执行函数声明过程,创建并用函数调的参数值或undefined初始化var声明,并绑定到varEnv中
4.创建新的env作用lexicalEnv,其外部作用域为 varEnv,并且context的lexicalEnv指向他
5.将function绑定到varEnv中
3.执行有默认值的函数流程
function test(name = 'dog', showName = () =>{console.log(name)}) { var name = 'cat' showName() } test()
上面代码就是包含了默认值的情况,请先思考下上面的输出结果是什么?
答案是:dog
这个答案有点反直觉,因为在函数体里面,已经把name改为cat了,为什么还是默认值的dog呢?
我们在看看下面代码的运行结果就有点头绪了
function test(name = 'dog', showName = () =>{console.log(name)}) { var name = 'cat' showName() // dog console.log(name)// cat } test()
可以从上面代码的运行结果看出,showName 和 函数体里面访问的name不是一个变量
其原理在于:在es规范中提到,当函数的参数有初始化器的时候, 会创建一个单独的参数作用域,用于绑定参数的声明,所以我们会得到上面的运行结果