Skip to content

Latest commit

 

History

History
208 lines (164 loc) · 5.54 KB

14 作用域&&作用域链.md

File metadata and controls

208 lines (164 loc) · 5.54 KB

作用域&&作用域链

PaddyWang

list:

[TOC]

简介

作用域是每一门编程语言重要的一部分,它决定了一个变量的有权访问

在JavaScript 中作用域又是面试常考的一个知识点

总结一下关于作用域的知识点:

从字面上来看作用域就是起作用的区域

即 :在函数执行时起作用的区域

先了解两个概念:

  • 块级作用域
  • 词法作用域

块级作用域

在 C 系的编程语言中,基本上都有块级作用域

所谓块级作用域,就是利用代码块来表示一个起作用的范围

在该范围内定义的变量,只允许在该范围内使用

在该范围外就无权访问该范围内定义的变量

这个范围使用花括号{ }来限定

在JS语言中不存在块级作用域

  {
      var num = 123;
  }
  console.log(num);  => 123 

词法作用域

在书写代码的时候决定变量的作用域

与运行时无关

以函数来划分作用域

  function fn(){
      var num = 123;
      console.log(num);  // => 123
  }
  fn();
  console.log(num);  // => Uncaught ReferenceError: num is not defined

既然函数可以划分作用域,那么通过函数的自调用就可以模拟块级作用域了

 (function(){
     var num = 123;
     console.log(num);  // => 123
  })();
  console.log(num);  // => Uncaught ReferenceError: num is not defined

那么在函数内再定义一个函数呢???

function parent(){
    var num1 = 123;
    function child(){
        var num2 = 456;
        console.log(num1);  // => 123
        console.log(num2);  // => 456
    }
    child();
    console.log(num2);  // => Uncaught ReferenceError: num2 is not defined(…)
}
parent();

conclusion:

在函数内部可以访问函数外部定义的变量,反之不行

作用域链

那如果在函数内部定义的变量和函数外部定义的名字一样怎么办???

var num = 1;
function parent(){
    var num = 123;
    function child(){
        var num = 456;
        console.log('1-' + num);  // => 1-456
    }
    console.log('2-' + num);      // => 2-123
    child();
    console.log('3-' + num);      // => 3-123
}
parent();
console.log('4-' + num);                   // => 4-1
  • 可以得出:

    • 函数内外定义的变量没有任何关系

    • 并且函数在访问的时候有一定的查找顺序

有了js中的作用域的划分

那么有联系的作用域就形成了一个链式的结构,即作用域链

作用域链的作用:保证变量的有序访问

在进行执行JS代码时,分两步执行

第一步:JS引擎会先对JS代码进行预解析(变量提升)

第二步:逐步执行

绘制作用域链
  • 绘制规则:
    • 作用域链就是变量(对象)的数组
    • 全部的<script>标签看成一个0级链
    • 每个变量占据一个位置
    • 因为只有函数划分作用域,所以函数会扩展出新的链,并一级级展开
    • 由于声明提升,因此绘制时所有声明提到该链的最前面
    • 在进行变量访问时,首先是查找该链是否有定义
    • 该链上如果有定义直接获取数据,如果没有查找上一级链,直到0级,再没有则报错

看一个demo:

var num = 1;
function fn1(){
    var num = 2;
    function fn2(){
        var num = 3;
        console.log('1-' + num);  // => 1-3
    }
    fn2();
    console.log('2-' + num);  // => 2-2
}

function fn3(){
    var num = 4;
    fn4();
}

function fn4(){
    console.log('3-' + num);  // => 3-5
}

var num = 5;
fn1();
fn3();
console.log('4-' + num);  // => 4-5

一步步分析:

在全局预解析,即0级上的声明有变量num,函数fn1fn3fn4,还有一些语句

这里的var num = 5;中的var无效,相当于num = 5;

函数fn1()又延伸出一条新链,即1级链,该链上的声明有变量num,函数fn2,还有一些语句

函数fn2()又延伸出一条新链,即2级链,该链上的声明有变量num,和一条语句

函数fn3()又延伸出一条新链,即1级链,该链上的声明有变量num,和一条语句

函数fn3()又延伸出一条新链,即1级链,该链上只有一条语句

即得到下面:

  • num = 1
  • fn1()
    • num = 2
    • fn2()
      • num = 3
      • console.log('1-' + num);
    • fn2(); console.log('2-' + num);
  • fn3()
    • num = 4
    • fn4();
  • fn4()
    • console.log('3-' + num);
  • num = 5; fn1(); fn3(); console.log('4-' + num);
变量搜索原则
  • 在代码的运行过程中, 如果访问某一个变量
  • 那么首先在当前链上找(无序), 如果没有,就在上一级找
  • ( 在函数内部允许访问定义在函数外部的变量 )
  • 如此往复, 直到 0 级链, 如果还没有 抛出异常
  • 如果找到, 则结束寻找, 直接获得该链上变量的数据

conclusion:

  • js中不存在块级作用域,遵循词法作用域原则
  • 以函数来划分作用域
  • 在代码书写时就已经决定了其作用域,与代码执行无关
  • 自调用函数可以模拟块级作用域
  • 变量提升
  • 两条同级作用域链之间没有任何关系
  • 下一级可以访问上一级变量,反之不行