20-作用域及预解析

一、函数作用域

作用域是一个变量或函数的作用范围。作用域在函数定义时,就已经确定了。
作用域的内部原理 
编译、执行、查询、嵌套、异常

  • 编译:边解释,便运行
    • 分词单元
    • 解析成抽象树
    //编译:
      //1.分词:var,a,=,10
      /*
          {
            'var':'keyword',//关键字
            'a':'indentifier',//标识符
            '=':'assignment',//分配
            '2':'interger',//整数
            ';':'eos',结束语局
          }
      */
      //2.解析
      //抽象语法树
全局作用域与局部作用域
  • 全局作用域:作用于整个 script 标签内部,或者作用域一个独立的 JS 文件
  • 局部作用域:作用于函数内的代码环境
作用域链

当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用(就近原则)。如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错。

    var i = 1//最后来全局找
    function a(){
      var i = 2
      //再来它最近的父级找
      function b(){
        i++//先在内部找
        console.log(i)//3
      }
      b()
    }
    a()
    
全局变量与局部变量
  • 全局变量:在全局作用域下面 声明 的变量,在script标签内所有的地方都可以访问和修改
  • 局部变量:在函数内部 声明 的变量,收到函数作用域的保护,只能在当前作用域访问
    • 两者生命周期
    • 全局变量:只有浏览器关闭时才会被销毁,比较占内存。
    • 局部变量:当其所在的代码块运行结束后,就会被销毁,比较节约内存空间。

函数的形参是局部变量

  <script>
    function foo(a) {
      return a + 6;
    }
    var res = foo(1);

    console.log(res);//7
    // 函数的形参是一个局部变量,全局中无法访问
    console.log(a);// a is not defined
  </script>

js中没有块级作用域

  <script>
    // js中没有块级作用域
    if (true) {
      var num = 123;
      console.log(num); //123
    }

    console.log(num); //123(可以正常打印)

    // 全局变量和局部变量仅存在函数中
    function fn() {
      var num1 = 123;
      console.log(num1);
    }

    console.log(num1);//num1 is not defined
  </script>

js函数调用栈

  <script>
    function a(){
      console.log('a')
    }
    function b(){
      console.log('b')
      // 函数内部的代码还是按顺序执行的,遇到新的函数执行才会进行压栈操作
      a()
      console.log('b1')
    }
    function c(){
      console.log('c')
      b()
      console.log('c1')
    }
    // 在函数执行的时候会将该函数压入到执行栈里面,当函数代码执行的时候又遇到了函数执行语句,那么当前函数执行
    // 上下文暂停,将新函数压入栈中...当函数执行完毕以后会发生出栈操作,函数的执行上下文交回给紧挨着    
		// 它的底下的函数
    c() // c b a b1 c1

js中没有动态作用域,是词法作用域

  <script>
    // js的作用域是词法作用域/静态作用域,而不是动态作用域!!!!
    // 函数作用域的形成是在函数声明的时候就决定好了的,而不是在调用的时候
    var i = 0
    function a(){
      console.log(i)
    }

    function b(){
      var i = 1
      a() // 0
    }
    b()
  </script>

二、函数的预解析

js在执行时,会将变量的声明和函数的声明解析到代码的最前面。

  <script>
    // 源代码
    var a = 1;
    var b = function () {
    }
    function d() {
    }

    // 预解析后
    var a;
    var b;
    function d() { }
    b = function () { }
    a = 1;
  </script>

函数是js中国的一等公民!当变量和函数同名时,解析时,只会执行函数

小练习
    var num = 10;
    fn1();
    function fn1() {
      console.log(num); // undefined
      var num = 20;//会在当前的函数作用域里面预解析
    }
    <script>
        // 先预解析变量a个函数aaa,bbb
        // 由三个aaa重名,所以最后一个会覆盖前两个,整个html只有第三个aaa和bbb生效
        // bbb中虽然var a= 40,但是调用的是aaa,aaa弹出的a的值访问不到函数bbb里面,所以是30
        var a = 10;
        function aaa() {
            alert('1' + a);
        };
        aaa();//310

        function aaa() {
            var a = 20;
        };
        aaa();//310
        alert('2' + a);//210

        var a = 30;
        function aaa() {
            alert('3' + a);//330
        };
        function bbb() {
            var a = 40;
            aaa();
        }
        bbb(); 
    </script>
    <script>
        function aaa() {
            a = 10;
        }
        aaa();
        alert(a);//undefined

        var a;//变量只声明不赋值,打印就是undefined

        function aaa() {
            var a = b = 10;
            // 解析为var a = 10, b = 10
            // 此时a是局部变量,b没有声明,是全局变量
        }
        aaa();
        alert(a);//undefined
        alert(b);//10
    </script>
    fn3();
    console.log(c); // 9
    console.log(b); // 9
    console.log(a); //  a is not defined
    function fn3() {
      var a = b = c = 9;
      console.log(a); // 9
      console.log(b); // 9
      console.log(c); // 9
    }
    a();
    function a() {
      console.log(1);//1
    }
    var a = 1;//将1赋值给a,此时a里面装的就是数,无法被调用~
    a(); // a is not a function
    console.log(a); // 压根不执行
    var n1 = 11;
    var n2 = 22;
    fn2();
    fn();
    function fn() {
      var n1 = 111;
      n2 = 222;//修改了全局里面的n2
      var n3 = 333;
      console.log(n1, n2, n3);//111,222,333
    }

    function fn2() {
      n1 = 1111;//修改了全局里面的n1
      var n2 = 2222;
      var n3 = 3333;
      console.log(n1, n2, n3);//1111,2222,3333
    }
    console.log(n1, n2);//1111,222
    console.log(n3);//n3 is not defined
  <script>
    if (true) {
      function foo() {
        console.log(true);
      }
    } else {
      function foo() {
        console.log(false);
      }
    }
    foo();
  </script>
  <script>
    console.log(a);//undefined
    a();//a is not a function
    var a = 3;
    var a = function () {
      console.log(10);
    }
    console.log(a);
    a = 6;
    a(); 
  </script>
    var num1 = 111;
    var num2 = 222;

    function fn(num1) {
      //函数的形参就是一个局部变量
      //预解析 var num1;
      num1 = 333;
      num2 = 444;
      console.log(num1); // 333
      console.log(num2); // 444
    }

    fn(num1, num2);

    console.log(num1); // 111
    console.log(num2); // 444
原文地址:https://www.cnblogs.com/xiaoaitongxue/p/12796846.html