面试说作用域、作用域链

面试必问题闭包、作用域、作用域链,这些知识点其实都是相互关联对应的。

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() {
    {
   		 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,)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值