50.函数的作用域链

要搞清楚一个变量的作用域,重点是搞清楚预解析顺序,然后再判断作用域的范围,这些都是有套路可言:先找本层环境有无声明,有的话,看是否进行了赋值只有声明没有执行赋值,就是undefined没有声明也没有赋值的话,就再向上一层查找,直到找到为止。如果所有的执行环境都没有找到,那么控制台就会报错变量找不到。

函数的话就更简单了:找本层环境是否有预解析的函数,有的话即可执行。没有的话还是向上查找。


6.什么是函数的作用域链 ?
javascript中,变量的作用域有全局作用域和局部作用域两种。
a.全局作用域:在代码任何地方都能访问到的对象拥有全局作用域
b.局部作用域:一般只在固定的代码片段内可以访问到。
c.函数作用域链:在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

1.全局变量:

     (1)在全局范围内声明的变量,如var a=1;

     (2)只有赋值没有声明的值,如a=2; (注:如果a=2在函数环境中,也是全局变量)

2.局部变量:

      写入函数中的变量,叫做局部变量。

应用场景(一些常见的作用域相关的面试题):

?
1
2
3
4
5
6
7
8
var a= "aa" ;
function test(){
  alert(a); //undefined,函数执行后,在函数环境内,var a会预解析,当弹出a时,首先先找本层环境内有无声明,发现有。但是代码没有执行到赋值,所以结果是undefined。
  var a= "bb" ; //var a会预解析在函数开头,执行到这行才进行赋值
  alert(a); //“bb”
}
test();
alert(a); //"aa" 找全局环境下的声明,找到了var a="aa"
?
1
2
3
4
5
6
7
var a= "aa" ;
function test(){
  alert(a); //“aa”,函数执行后,在函数环境内,没有找到本层环境关于a的声明,所以开始向上一层环境查找。
  a= "bb" ; //执行到这行开始改变全局a的量
}
test();
alert(a); //"bb" 全局环境的a在函数执行时已经被改变
?
1
2
3
4
5
6
7
8
9
10
function test(){
  b(); //函数b会被预解析,因此可以调用,执行了输出1;
  var a=1;
  function b(){
   console.log(1);
   console.log(a); //undefined
   var a=2;
  }
}
test();

alert(foo); // function foo() {}
alert(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
alert(foo); // function foo() {}
alert(bar); // function bar_fn() {}

输出结果分别是function foo() {}undefinedfunction foo() {}function bar_fn() {}

可以看到 foo 的声明是写在 alert 之后,仍然可以被正确调用,因为 JavaScript 解释器会将其提升到 alert 前面,而以函数表达式创建的函数 bar则不享受此待遇。

那么bar 究竟有没有被提升呢,其实用 var声明的变量都会被提升,只不过是被先赋值为 undefined 罢了,所以第二个 alert 弹出了 undefined

var a = 1
function fn1(){

  function fn3(){
    function fn2(){
      console.log(a)
    }
    var a

    fn2()
    a = 4
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //输出undifined


var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)
}
var fn = fn1()
fn() // 输出1

var a = 1
function fn1(){
  function fn2(){
    console.log(a)
  }
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
var fn = fn1()
fn() //输出2

以下代码输出什么? 写出作用域链查找过程伪代码

var a = 1;

function fn(){
  console.log(a) //(1)
  var a = 5
  console.log(a) //(2)
  a++
  var a
  fn3() //(3)
  fn2() //(4)
  console.log(a) //(5)

  function fn2(){
    console.log(a)
    a = 20
  }
}

function fn3(){
  console.log(a)
  a = 200
}

fn()
console.log(a) //(6)
  • 输出结果:从上到下依次是undefined、5、1、6、20、200
  • 查找过程:
    • 调用函数fn,进入fn的执行环境,var a变量声明前置并初始化a为undefined,所以(1)处打印undefined;
    • 给a赋值5,所以(2)处打印5;
    • 继续执行a++,得到a为6留到下次使用
    • 继续执行函数fn3(),进入fn3作用域,fn3内没有声明变量a于是向外层查找,而fn(3)所处作用域为全局作用域,无论它在哪执行,永远都是打印的全局变量1,所以(3)处打印1,继续向下读取a=200,fn3不存在函数嵌套的情况,里面的a也未被声明,所以a是一个全局变量,全局中声明了变量a,于是替换全局变量a的值1为200,所以(6)处打印200;
    • 接着调用fn2函数,进入fn2执行环境,没有找到变量a,于是向外层函数fn中查找,得到了上上一步中的值6,所以(4)处打印6,继续向下读取a=20,a未被声明于是会逐级向上查找变量a,在父函数fn中找到了变量a并赋值20,所以(5)处打印20。

分析:fn3改变了全局变量a的值,fn2改变了fn中局部变量a的值。作用域(执行环境)全局变量是相对的。



作者:_Dot大师兄
链接:https://www.jianshu.com/p/ac38114b6e24
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


作者:萧雪圣
链接:https://www.jianshu.com/p/888fded98527
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


作者:betterwlf
链接:https://www.jianshu.com/p/70a7c75b863e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值