⾯试说作⽤域、作⽤域链
⾯试必问题闭包、作⽤域、作⽤域链,这些知识点其实都是相互关联对应的。
1,作⽤域概念
当⾯试时闻到这个问道什么是作⽤域时,可简短回答:作⽤域指定了程序中变量的⽣命周期和适⽤范围。
在es6以前,js的作⽤域只有 函数作⽤域和全局作⽤域,es6⾥新增了块级作⽤域。
作⽤域链:由于作⽤存在着嵌套(⽐如函数嵌套另⼀个函数),所以js引擎在查变量时会先查当前作⽤域内,如果查不到会查外层作⽤域内是否含有,直到查到全局作⽤域。这就形成了作⽤域链的概念。
接下来我们详细来讲解。
2,理解作⽤域
js引擎在执⾏js代码之前,编译器会先对js代码进⾏词法分析、语法分析和代码⽣成。(具体这部分的内容可以查看资料,简单了解下。)。
编辑器在词法分析会把⼀段代码分解成词法单元,然后把词法单元(token)解析成树结构,⽽在代码⽣成式时遇到变量,会把变量存在当前环境作⽤域内并⽣成引擎可以执⾏的代码。引擎在执⾏代码时会先去作⽤域中查当前变量是否存在,之后进⾏下⼀步操作。
⽐如遇到var a=1;这段程序时。
1,编译阶段,编辑器遇到var a的时候会先进⾏变量提升,把a放到当前执⾏环境的作⽤域内。
2,引擎执⾏阶段 遇到a=1;会先去作⽤域内查,是否有变量a,如果有,就把1赋值给它。
3,理解作⽤域链
function f1(a){
var c=1
console.log(a+b+c)
}
var b=2;
f1(1);//1
遇到上诉代码时,整个流程如下,
1,编译阶段,编译器遇到function和var时,会先获取这些变量的定义,把b,和f1函数放⼊全局作⽤域内。
2,引擎执⾏代码时,
1)遇到b=2,在作⽤域内查变量b,并将2赋值给b。
2)遇到f1(1)时,会进⼊f1函数的执⾏上下⽂,进⾏执⾏f1,当执⾏到console.log时,调⽤栈的环境如下图所⽰。
如上图所⽰,我们在执⾏console.log( a + b +c )时,引擎查b时,会现在f1函数的作⽤域范围内查,不到就会去全局作⽤⾥查。在全局作⽤域⾥,到b=2。这就是所谓的作⽤域链式查。
4,作⽤域链形成规则
这⾥⼜会有⼀个问题,怎么样才算作⽤域的嵌套呢?形成的是作⽤域链呢。⽐如
function f2() {
console.log(myName)
}
function f1() {
var myName = "f1变量"
f2()
}
var myName = "全局变量"
f1() //全局变量
如上诉代码,我们执⾏后发现f2虽然嵌⼊在f1函数⾥,但是f2⾥的myName取值并不是f1中的myName,⽽是全局的变量myName。
这⾥涉及到⼀个概念:作⽤域链是由词法作⽤域决定的,⽽词法作⽤域就是指作⽤域由代码中函数声明的位置来决定的,所以词法作⽤域也成为静态作⽤域。(也可以理解为在词法⽣成阶段就已经决定了作⽤域的位置。也就决定了作⽤域链)
(词法作⽤域-----编辑器的第⼀个⼯作阶段是词法化,词法化阶段会对源代码中每个字符逐个检查,如果有状态的解析,还会赋予这个词⼀定的意义。这也是词法作⽤域名称的来历)
其实每个作⽤域⾥都有⼀个outer指向它的上⼀级作⽤域,这个指向是按照代码书写位置来决定的。
当执⾏到console.log(myname),调⽤栈信息如下图所⽰
如图所⽰,outer指针指向的是上⼀级作⽤域,是由代码书写位置来决定的。也就是说作⽤域链由代码书写位置决定,和函数调⽤没有关系。
⼀般来说词法作⽤域由代码书写位置决定的,不过也有两种机制会在运⾏时改变作⽤域链,eval和with,不过这两个都不建议⽤。这⾥我们就不讨论了。
作⽤域由代码书写位置决定的好处是,可以在编辑阶段进⾏⼀些优化。
5,es6的块级作⽤域
在es6之前,只有全局作⽤域和函数作⽤域的概念,在es6中新增了块级作⽤域。在es6中由{}包括的都属于块级作⽤域。(不过var在块级作⽤域⾥没有任何意义,let和const有⽤。)
块级作⽤域⼀般属于全局作⽤域或者函数作⽤域⾥。
function f2() {
const的作用{
let myName="f2块级作⽤域"
}
{
let m="2"
console.log("f2-2=",myName)
}
}
function f1() {
var myName = "f1变量"
f2()
}
var myName = "全局变量"
f1() //f2-2=全局变量
如上诉代码所⽰,输出结果:f2-2=全局变量;当执⾏到 console.log(“f2-2=”,myName)时,调⽤栈如下图所⽰。
如上图所⽰,当执⾏console时,会在当前块级作⽤域查,如果查不到就去函数的整体环境变量⾥查,如果没有就通过作⽤域链继续查上⼀级。查顺序也就是图中1、2、3、4.
总结:作⽤域分为全局作⽤域、函数作⽤域、es6的块级作⽤域。作⽤域和作⽤域链取决的代码书写的位置,⽽不是调⽤位置(也有执⾏时改变作⽤域例如even和with,)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。