自学js第九天:JS函数作用域详解

本文深入探讨JavaScript中的全局与局部作用域,详细解析变量寻址规则,包括函数内部作用域、变量声明提升、预解析过程以及作用域链。通过实例分析变量在不同作用域的访问权限,阐述了预解析时声明语句与执行语句的顺序,以及函数内部如何查找变量声明。此外,还介绍了块级作用域、词法作用域和链作用域的概念,帮助理解JavaScript中复杂的变量作用域机制。
摘要由CSDN通过智能技术生成

JS作用域:变量可以起作用的范围

一.全局变量和局部变量:

  • 全局变量:

    在任何地方都可以访问到的变量就是全局变量,对应全局作用域
  • 局部变量:

    只在固定的代码片段内可访问到的变量,最常见的例如函数内部的变量。对应局部作用域(函数作用域)
不使用var声明的变量是全局变量,不推荐使用。
变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁

1.JS全局作用域和局部作用域案例:

1.1javascript 有作用域概念:
1. 全局作用域(script标签内)有局部作用域 (function函数{})

2.局部作用域内可以访问全局变量, 即全局变量在哪都能访问,而私有变量只能在当前作用域内访问,出了 

当前作用域则 六亲不认,无法访问报错.

1.2 怎么寻找一个没有var 声明符的变量的变var量声明:
  1. 首先先在当前的fn作用域上下寻找 x 的声明(同一作用域因为可以先斩后奏的,先输出再声明) =>找得到我就使用当前域的声明即就近原则 => 没有找到(则确定x不是该函数域的局部变量)

  2. 则向上一级作用域寻找 x 的声明 一直一层一层找到 全局作用域 => 如果也没有声明
    (PS:上一层的全局变量,是有带赋值的 var x =10;)

  3. 最终都没找到,则js引擎会在全局进行隐式声明,var x; , 默认值为undefined,也是全局变量了.
    (PS:js引擎默认给全局变量的隐式声明,无赋值的.var x;)

PS: 因此隐式声明不能直接在局部作用域内使用 var ,要先一层一层看,最终没办法才去’全局’变量隐式声明. (很多时候局部内部的无var声明符的变量都是全局变量,到哪都能访问的. 下面有案例)
1.3全局和局部作用域总结:
1. 在javascript中 有全局作用域(script标签内)有局部作用域 (function函数{})

2. 作用域是单向可访问 内部作用域可以con访问外部作用域的全局变量 外部作用域不可以con访问内部作用域的私有变量

3. 局部私有变量 指的是 在当前局部作用域下有var声明的变量 ,出了该作用域就六亲不认.
 PS:方法内部的变量如果没有var声明符就不是当前作用域的私有变量.
 
4. 局部私有变量名x,在多个作用域内可以重复声明的,但是各自管各自的私有变量,不会去覆盖,它们之间一点关系都没有 , 除非同一作用域下单局部变量重复声明才会去覆盖. 
 
 //456点怎么找变量的作用域, 自己有的则优先用自己的,自己没有的就去上一级找,都没有引擎给你隐式
 
5. 方法内部作用域的有声明符号var的局部变量的访问, 遵循优先就近原则,访问自己作用域内的局部变量.

6. 如果一个变量x 在局部作用域中有赋值 ,没有var声明,但是此时注意如果变量是有形参x的话,也是算是:
 局部变量在内部有一个var x 局部隐式声明,只是形参默认值为undefined     function a(x) { x = 100;}

7. 如果一个变量x 在局部作用域中有赋值 ,但是没有var声明, 则会向上一直寻找到 全局作用域 
也没有 var x声明 ,js引擎会在全局作用域顶端进行隐式声明var x;    function a() { x = 100;}

8.如果一个变量x,在局部作用域中即没有赋值,也没有var声明,就直接去输出,过分了直接报错没有定义defined.
1.4 如何寻找一个没有var 声明符的变量的作用域案例1:(即变量寻址案例)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作用域</title>
</head>

<body>
  <script>

     
    //案例1: 
    var w = 10;
    function fn() {
      var x = 10; //x 属于fn这个小世界里的变量10
      console.log(w);//10
    }
    fn();
    console.log(x);//报错,私有变量出了作用域无法访问报错.
    console.log(w);//10

 
    //案例2: 上一层全局变量,是有赋值的.
    var x = 10;
    function fn() { //副作用函数,值进来变了
      x = 20; 
      //=> 这是全局变量x (原因:因为当前作用域没有声明符号var ,即x不是局部变量,x跳出该作用域
      //去上一层找到声明 var x,则寻找到了全局变量声明 var x,则确定x是全局变量, 因此就是修改了上面全局变量x的值)
    }
    console.log(x);//10 ,因为没有调用方法去修改全局变量的值
    fn(); // 进入fn函数,修改了全局变量 x 的值
    console.log(x);//20
    

    //案例3: 局部
    var x = 10;
    function fn() {
      var x; 
      x = 20; //这里x是局部变量,原因:当前域就近找到了var x声明,即x就是指向fn域私有的局部变量
      console.log(x); //20  就近原则访问
    }
    fn(); //进入fn函数,但只是修改了fn函数域局部变量 x 的值,
          //因此不影响fn作用域外即下面的全局x输出
    console.log(x);//10



    
    //案例4: js引擎默认给全局隐式声明,无赋值的.
    var x; //这行是没有的,js解释引擎加上的最终的隐式声明 因此默认值也为undefined了.
    function fn() {
      x = 10; //当前作用域没var声明符,向上一层也没全局var x=xx声明; ,因此只能js引擎给var x;默认undefined
    }
    console.log(x); //undefeined,隐式全局声明时undefined默认值
    fn();
    console.log(x); //10,调用函数修改全局值为10.




    //案例5:
    var y; //这行是没有的,JS隐式声明.
    function fn() {
      //x是fn局部作用域的私有变量 var x =10;
      //y 当前作用域没有声明符var, 但有赋值, 则向上层作用域寻找,
       var x = y = 10;
      // x 是局部私有变量 y是通过隐式声明的 全局变量
        
      /* 
      var x = y = 10;如何解析的,先声明语句后执行语句,
      var x;  
      var y;   实际y是要去全局顶端声明的.
      y = 10;
      x = y; */
    }
    fn();
    console.log(y); // 10,判断为全局变量.因此可以访问
    console.log(x);//....超出作用域x无法访问到, 因为 x是 fn作用域的私有变量



    //案例6:
    var x = 10;
    function fn() {
      console.log(x); //10 此时还未修改全局变量,还是10
      x = 20;// 到此处,有赋值但是没有声明的变量x,则是向上找,是全局变量,则该修改全局变量x的值 为20
      console.log(x);//20
    }

    fn();
    console.log(x); //20 ,因为是全局变量,因此出了作用域外部还是生效能访问全局变量x

    

  </script>
</body>

</html>
1.5变量寻址案例2:
   /*
     判断 是全局变量 , 还是局部变量 
	
    */
    //判断1:
    // var x = 10;
    // function fn() {
    //   console.log(x); //全局
    // }

     //判断2:
    // var x = 10;
    // function fn() { //不同作用域可以二次重复声明
    //   var x = 20;
    //   console.log(x); //局部,就近原则输出局部
    // }


    //判断3:
    // function fn() {
    //   x = 10;
    //   console.log(x); //1
    // }

1.6变量寻址案例2:
/* 复习:
	1.之前海牙老师都是,任何一个声明变量步骤都是先声明,再赋值.
	var x = 10;
    分解为:
    ① var x;  
    ② x = 10;
    
    2.现在可以这么写
     ② x = 10;
     ① var x;  */
     
   //案例1:
    var x=100;
    function fn(){
      x=10;   //此时这也是局部变量
      var x;     
    }
    fn();
    console.log(x); //100,此时进入函数修改的是局部变量,因此这里还是输出全局变量100.
    
   //案例2:
    var y=100;
    function fn(){
      y=10;   //而此时这是全局变量 
    }
    fn();
    console.log(y); //10 ,此时进入函数修改的是全局变量,输出的就是修改过后的全局变量.
 

2. js引擎怎么预解析代码,提升变量: (类似java弹栈和压栈)

2.1.什么叫预解析: (类似java虚拟机编译过程)

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程

2.2.预解析详细过程和案例:(包含全局解析规则 + 函数内部解析规则)

一总得流程:一层一层作用域解析:先解析全局的作用域的srcipt ,然后才解析局部的作用域的函数fn.

1.解析全局作用域分为解析: 全局声明语句 和 全局执行语句
1.1开始压栈全局声明语句:寻找所有的(变量和函数)全局声明语句,并提升他们到自己当前作用域的最顶端 
   具体全局作用域的声明语句:1. 先提升全局变量声明  2. 再提升函数声明
1.2开始弹栈全局执行语句:  
  具体全局作用域的执行语句: 1.执行语句就按照自上而下的顺序,声明无顺序全部置顶,(有赋值,输出,调用,控制语句等)
 
(前提如果在全局执行语句中有调用函数,才进入这一步) 
2.解析局部函数作用域分为解析: 局部声明语句 和 局部执行语句
2.1开始压栈局部声明语句:寻找所有的(变量和函数)局部声明语句,并提升他们到自己当前作用域的最顶端 
   具体局部作用域的声明语句:1. 先提升局部变量声明  2. 再提升内嵌的局部函数声明(涉及作用域链)
2.2开始弹栈局部执行语句:  
  具体局部作用域的执行语句: 1.执行语句就按照自上而下的顺序,声明无顺序全部置顶,(有赋值,输出,调用,控制语句等)

PS作用域解析小细节:

1.声明语句:先提升声明var,在提升function声明(注意 函数表达式即匿名函数,是属于声明变量的)
 var x = function () {x = 90;}   //这个匿名函数即函数表达式,是属于声明变量置顶提升的.
 //预解析  是先声明语句: var x;   =>   再执行语句:x = function () {x = 90;}
         作用域
            表示变量可以发挥作用的域 能够对某个变量寻址(调用 赋值)正常访问的 域
            1. 作用域内可以访问上级作用域的(变量 函数)
            2. 上级作用域区域不可以调用下级作用域内的 (变量 函数)
            3. 一个函数 就是一个作用域 if() for() 这些控制流程语句 不构成作用域
2.特殊,如果函数名x要是和变量名x重合,则解析时在’内部’等于开辟一个局部声明地址, var x.
3.特殊,如果函数名x要是和变量名x重合,则解析时在’外部’会覆盖掉前面其他的x变量声明.
4.超级坑:!!! (很容易忽略的细节,即fn();调用函数,函数内的值的修改才会生效的.)
如果一个函数没调用,是不会进入里面改变值的,只有fn();调用函数才会去局部解析函数否则函数不会进入.
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作用域练习</title>
</head>

<body>
  <script>
 
    var x = 10;
    function fn() {
      //var x; 提升局部变量26行的来这
      x = 20; 
      console.log(x); //这里打印值是多少? 20
      var x = 30;
    }
    x = 30; //属于21行的var声明符
    fn();
    console.log(x); //这里打印值是多少?  30


    /*============================分割线========================== */
    //var x;提升全局变量
    console.log(x);//这里打印值是多少? 
    var x = function () {
      x = 90;   //全局
    }
    console.log(x);//这里打印值是多少? 
    x();
    var x = 30;  //全局

    function x() {
      //var x; 函数名和变量名重合则有局部隐式声明
      x = 40;  //(很特殊,函数名要是和变量名重合,则内部等于开辟一个局部声明地址, var x)
    }
    console.log(x); //这里打印值是多少? 

    
    /*全局解析
    //声明语句,(变量和函数声明不按顺序,全部先提取到顶端)
    var x;   //全局隐式
    var x;  //function () { x = 90;} 函数表达式实际是一个变量声明,先提升变量  
    var x; //30
    function x() { // 然后再提升函数.
      x = 40;  
    }
    
    
    //执行语句,(执行才是自上 x=90而下按顺序)
    x= undefined;
    console.log(x); //注意:为什么不是undefined? 因为函数名x要是和变量名x重合,则外部会覆盖掉前面的x声明.
                    //因此这里是函数体,输出函数体function () {  x = 40; } ,如果不重合这里本应是undefined的.
    x = function () { x = 90;}
    console.log(x);  // 覆盖输出函数体function () {  x = 90; } 
    x();      //局部40
    x = 30;   //全局30
    console.log(x); //因此这里是全局的30
    */

    

    /*============================分割线========================== */
    //var x;  提升全局变量到顶端先
    var x = 20; //全局
    function fn1() {
      console.log(x); //这里打印值是多少?  全局  40
    }
    x = 30; //全局
    function fn2() {
      var x = 1;  //局部
      fn1();
    }
    x = 40; //全局
    fn2(); //通过先调fn2  ,进入再调fn1,
    
   
    /*全局解析
    //全局声明语句,(变量和函数声明不按顺序,全部先提取到顶端)
    var x;  //函数表达式实际是函数声明,先提升变量  undefined
    function fn1() {  // 然后再提升函数.
      console.log(x);  //全局已经被修改为40
    }
    function fn2() {  // 然后再提升函数.
      var x = 1;  //局部
      fn1();
    }

   
    //全局执行语句,(执行才是自上而下按顺序)
    x = 20;
    x = 30;
    x = 40;
    fn2(); 
   
    //局部fn2();解析
    //声明语句
    var x;
    //执行语句
    x = 1;
    fn1();
    console.log(x); //这里是全局,此时已经修改为40了

    */


    
    
    /*============================分割线========================== */
    var x = 20;  //全局
    function fn() {
      //var x;提升声明var x = 30;到这
      if (x) { //undefined ,为false,这里不执行
        var x = 30;  
      }
      console.log(x);//这里打印值是多少?  提升变量变为undefined的.
    }
    fn();
    console.log(x);//这里打印值是多少? 20  (局部不会影响全局的值,反之也是.)
    
 /*全局解析
    //全局声明语句,(变量和函数声明不按顺序,全部先提取到顶端)
    var x;  //函数表达式实际是函数声明,先提升变量  undefined
    function fn() {  // 然后再提升函数.
    }
   
    
    //全局执行语句,(执行才是自上而下按顺序)
    x = 20;
    fn();
    console.log(x);

  局部深入解析fn();
    //声明语句
    var x;
    //执行语句
    if (x) {
      x=30  (不能拿上去,因为if条件不确定,要写是一个整体的)
    }
    console.log(x);
    

    /*============================分割线========================== */
   
    function fun(param) {
      console.log(param);//这里打印值是多少?  5
      var param = function () {
        console.log(1);
      }
      console.log(param);//这里打印值是多少?   ƒ () {console.log(1);}
    
    }
    fun(5); //调用该函数

    /* 全局解析:
     //声明语句
     // var param; 
     //function fun(param) {} 提升函数
     
     //执行语句
     //fun(5);


     局部fun(5)解析:
     //声明语句
     //var param; 形参的默认隐式声明,默认值undefined
     //var param; 函数表达式也是变量, 
     
     //执行语句
     //param=5; 实参传进来
     // console.log(param);  // 实参的5
     //param = function () {console.log(1);}
     // console.log(param); //覆盖修改输出的是函数体 function () {console.log(1);}
    */


    

    /*============================分割线========================== */
    var foo = 1;  //全局
    function bar() {
      function foo(){ 
      foo = 10; //局部,因为特殊的:内部函数名和变量名重合,内部开辟一个局部声明地址。
      console.log(foo);//这里打印值是多少? 10
    }
  }
    bar();
    console.log(foo);//这里打印值是多少?  1 

    
   /* 全局解析:
     //声明语句
     // var foo; 
     //function bar() {} 提升函数
     
     //执行语句
     //foo =1;
     // bar(); //内部都是局部,因此bar不影响下面一行的全局
     //console.log(foo);   //1


     局部bar()解析:
     //声明语句
     //function foo(){ }
     
     //执行语句
     //无

     //局部foo()解析:
     //声明语句:
    //var foo (很特殊,函数名foo要是和变量名foo重合,则等于内部开辟一个局部声明地址, var foo)

    //执行语句
    //foo = 10; 
    //console.log(foo);  //10


    
    /*============================分割线========================== */
    //var b;
    function fn() {
      //var a; a是局部提升
      //b=3;
      //a=b;
      var a = b = 3;
    }
     //注意这里是fn没被调用, 因此都是fn内部都是没有执行的.
    console.log(b);//这里打印值是多少?  报错,未定义
    console.log(a);//这里打印值是多少?  报错,未定义


     
    /*============================分割线========================== */
    //var b; //全局
    function fn() {
      //var a; a是局部提升
      //b=3;
      //a=b;
      var a = b = 3;
    }
    fn();
    console.log(b);//这里打印值是多少?  3
    console.log(a);//这里打印值是多少?  报错
  </script>

</body>

</html>
2.3综合预解析案例2:
<script>
    var x = 30;
    function fn() {
      x = 10;  //会修改全局
      function fn1() {
        console.log(x + 'fn1'); //10fn1 (fn1作用域内的x全局变量)
        x = 20; //全局修改为20了
      }
      fn1();
      console.log(x + 'fn'); //20fn   (fn作用域内的x局部变量)
    }

    fn();
    console.log(x + 'script');//20script  (fn1作用域内的x全局变量)



    //JS引擎如何 压栈和弹栈 ,先进后出,后进先出./

    //开始压栈.
     //先提升变量声明和函数声明. 然后再有赋值和调用执行语句
    /* =======================全局================ */
    var x;
    function fn() { /* 不管内部先 */ }


    x = 30;  //30
    fn();
    
    
    /* =======================fn=================== */
    function fn1() { }

    x = 10; //10
    fn1();
    /* =======================fn1=================== */
    //没东西解析.

    
    //开始回溯,弹栈.
    /* =======================fn1=================== */
    console.log(x + 'fn1'); //10
    x = 20; //20


    /* =======================fn=================== */
    console.log(x + 'fn'); //20

    /* =======================全局================ */
    console.log(x + 'script');//20






  </script>
2.4JS解析案例2:(简单的压栈和弹栈)
var a = 25;
function abc (){
  alert(a);//undefined,因为下面声明了优先用自己的,可以先斩后奏,但是会给默认值undefined。
  var a = 10;
  console.log(a);//10  局部
}
abc();

console.log(a); //25 全局 ,里面全是局部,影响不到全局


// 升级:如果变量和函数同名的话,函数优先
function a() {
  console.log(a);  //不输出,没调用a方法
}
var a = 1;
console.log(a); //1


//解析:
//全局声明
var a
function abc (){}
function a() {}  //一旦被被调用则覆盖前面a变量
var a 
//全局执行
a = 25;
abc(); //调用完才能知道下面的结果的   局部声明:var a    局部执行:a = 10;   console.log(a)
console.log(a);  //这里a是全局 25
a = 1; //全局修改为1
console.log(a); //1  全局
2.4JS预先解析案例3:
 <script>
    
    //什么是声明语句,什么是执行语句
    //var x; 预解析 
    x = 10; // 执行语句 (赋值)
    console.log(x);// 执行语句 (调用)
    var y; //声明语句 
    y = x + 10; //执行语句
    console.log(y);//20
    var x;//这里二次声明没赋值,则无效的,写了值则会覆盖

   
    // 预解析提升1:
    // var x; ///这句话是不存在的提升,这里的默认的隐式声明语句被提升
    x = 10;//遇到x=10先不会赋值,会先寻找声明语句,提升上去,如上

   
    // 预解析提升2:
    //var x;  //这些句话是不存在的提升
    //var y;
    //  y =10;
     // x=y;
    var x = y = 10;
    


    // 预解析提升3:
    // x = 10;
    //var x; //这句话是不存在的提升,变量先斩(输出)后奏(赋值),只是为默认值undefined
    console.log(x); //执行 undefined
    x = 10; //执行 x值变为10
    console.log(x); // 10




  </script>
2.5预解析案例4:
先判断变量所在的作用域范围+ 全局/局部变量 + 提升声明语句到当前作用域顶端 + 最后再轮到执行语句
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作用域 结合 解析 声明提升</title>
</head>

<body>
  <script>

  

    //案例:我们自己手写的代码,但是js引擎并内部不会这样按顺序直接解析,
    var x = 10;
    function fn() {
      console.log(x); //undefined  ,为什么是undefined????
      var x = 20;
    }
    fn();
      console.log(x); //10 ,为什么是10???/


    /* =====================================  */ 

      
    //在js引擎内如何解析上述方法.
    //一.先全局域分析:  (类加载)
    var x; //1.1先找到并且提升js整个全局变量声明var x,到当前全局作用域的顶端
    function fn() { //1.2再提升函数声明.
   //二.再fn函数局部域再分析:
      var x;        // 2.1 拆分var x = 20;,找到声明提升局部变量声明var x;到当前fn函数作用域的顶端
      console.log(x); // 2.2 最后再输出执行语句.(PS:x是判断为局部变量, 是undefined默认值 )
      x = 20; //2.3 最后再赋值执行语句,赋值为20.
    }
    x = 10;// 1.3.再去执行全局变量的赋值代码,因为要先提升 1.1变量和函1.2函数声明先。
    fn(); //1.4.再去执行调用方法执行代码
    console.log(x);//1.5 再去执行输出方法执行代码 ,10



  </script>
</body>

</html>
2.6:综合预解析案例5: (关乎作用域链的预解析 , 作用域链要回溯方法解析 ,下面案例有)

   //作用域链:函数套函数.
    // var x = 10;
    // function fn() {
    //   var x = 20;
    //   function fn1() {
    //     x = 30;
    //     function fn2() {
    //       var x = 40;
    //       function fn3() {
    //         x = 50;
    //       }
    //       fn3();
    //     }
    //     fn2();
    //   }
    //   fn1();
    // }
    // fn();


    
    var x = 30; //全局作用域x
    function fn() { //作用域 fn
      var x;
      x = 10; 
      function fn1() { // fn作用域里面的fn1作用域
      console.log(x); // 上一层的局部变量10
        x = 20;           //局部修为20,fn1才结束,回溯回去解析fn 
      }
      fn1();       
      console.log(x);// 20  上面的局部20,fn这里才结束,回溯回去解析全局
    }

    fn();//虽然调用fn,但内部全是都是局部变量,影响不到下面的全局变量.
    console.log(x);// 30  全局变量

   //进入 fn()  按顺序还是要去进入fn1()先输出fn1的con ,在输出fn的con,最后才是最后一个con




    
    /* ============= 全局解析 ================== */
    //全局声明
	var x;
    function fn() {
    //.................不管
    }
    //全局执行
    x = 30;
    fn(); //先进入fn()才能得知下面的con最终结果
    console.log(x);


    /* ===============  fn局部解析  ================ */
	//局部声明
    var x;
    function fn1() { // fn作用域里面的fn1作用域
      //.......
    }

    //局部执行
	x = 10;
	fn1();  //同理这里要先进入 fn1() 才能得知下面的con最终结果
    console.log(x);//20 


    /* ===============  fn1局部解析  ================ */
    //无任何声明了.
  

   //执行语句开始回溯,弹栈.
    console.log(x); //10
    x = 20; 


    /* =======================fn=================== */
    console.log(x); //20

    /* =======================全局================ */
    console.log(x);//30


 
    //x => 寻址过程
    /*
      1. 当前作用域 是否有 x的声明
      2. 向上一级作用域寻找x的声明 fn
      3. 在fn作用域内寻找到了 x的声明
      4. 确定这里的x是 fn的私有变量 x
      5. fn的私有变量x 的值被赋值为 20

     */
2.8综合预解析案例7: (超级阴险题目)
<script>


    /*
      变量 x 和函数x重名
      1. 寻找所有的声明 并且提升
      2. 先提升变量 然后提升函数
    */


   //坑人案例1:变量 x 和函数x重名时 ,后来者居上,后面 函数x 会把var x 覆盖掉.
    var x = 10;
    function x() {
      x = 20;
    }
    x();
    console.log(x); //报错


    /* ===================全局=================== */
    //压栈
    var x;
    function x() { }  //本来是x()函数覆盖var x声明的, 但是下面弹栈又赋值x为10了,因此10();还是报错
    //弹栈
    x = 10; //10
    x(); //10()    
    console.log(x);//报错  x不是a function函数,这题GG没了,坑人的


  
    

   //坑人案例2: 变量 x 和函数x重名时 ,后来者居上,后面 函数x 会把var x 覆盖掉.
    console.log(x); //undefined
    var x = 10;
    function x() {
      x = 20;
    }
    x();
    console.log(x); //报错

 
    

  // 变量 x 和函数x重名时 ,后来者居上,后面var x会把 函数x覆盖掉.
    function x() {

    }
    var x; //undefined 

    console.log(x); //undefined


  </script>
3.大总结函数基础 和 函数变量 和 函数作用域 和 函数的预解析 :


  <script>
    /*
      回顾 函数 作用域

      函数
       函数体
        function 函数名(形参){
          //函数内部代码
          return 值; //返回关键字
        }

        函数名(实参);

       作用
          封装代码 方便重复调用

       参数
          能够让我们的函数处理不同的原始数据 从而得出不同的结果

         形参
           自定义命名 个数随意 和实参对应位置
           默认值 undefined
           形参会在 函数内隐式声明为局部变量
         实参
           函数调用执行的时候传入的实际参数

          不定参 arguments
          实参列表的集合 多个子项形成的集合 很像数组
          类数组 有length属性 能够通过下标取值 但是不是数组 不能使用数组的方法


       return
          函数执行到return的时候 返回一个值
          默认返回为undefined 只写return 后面 ; 也是返回undefined
          return 后指定一个值 (字面量 变量名) 返回变量内存储的值

          函数内代码执行到明确的return语句就结束了 return语句之后的语句不会再执行了


        纯函数 非纯函数 匿名函数 自调用IIFE 基础柯理化



        作用域

          作用域
            表示变量可以发挥作用的域 能够对某个变量寻址(调用 赋值)正常访问的 域
            1. 作用域内可以访问上级作用域的(变量 函数)
            2. 上级作用域区域不可以调用下级作用域内的 (变量 函数)
            3. 一个函数 就是一个作用域 if() for() 这些控制流程语句 不构成作用域


            预解析
              1. 先寻找当前域的所有 声明 (变量 函数) 并且提升
                1. 变量和函数重名的时候 先提升变量 然后提升函数 => 函数覆盖变量
              2. 按照书写顺序执行当前作用域下所有的执行代码
                1. 变量没有在当前作用域声明, 会向上一级作用域查询 依次查询到全局作用域
                如果全局作用域也没有对应变量的声明 => 隐式声明该变量在全局作用域并提升到最顶端
              3. 对应局部作用域解析需要 执行到 对应函数执行的那一行 再解析


          作用域链
            1. 作用域嵌套为函数嵌套关系
            2. 当前作用域内变量调用优先调用自身局部变量 如果自身局部作用域没有声明 向上一级查询



  */


  </script>
</body>

</html>

二.块级作用域:(局部作用域)

任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
在es5之前没有块级作用域的的概念,只有函数作用域,现阶段可以认为JavaScript没有块级作用域

三.词法作用域(类静态作用域)

该变量的作用域是在定义时决定,而不是在执行时决定,
也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。

在 js 中词法作用域规则:

  • 函数允许访问函数外的数据.
  • 整个代码结构中只有函数可以限定作用域.
  • 作用域规则首先使用提升规则分析
  • 如果当前作用规则中有名字了, 就不考虑外面的名字
var num = 123;
function foo() {
  console.log( num );
}
foo();

if ( false ) {
    var num = 123;
}
console.log( num ); // undefiend

四.链作用域(方法作用域套娃方法作用域)

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。
// 基础布局案例1:
function f1() {
    function f2() {
    }
}

var num = 456;
function f3() {
    function f4() {    
    }
}

在这里插入图片描述

// 基础简单案例2
function f1() {
    var num = 123; //局部
    function f2() {
        console.log( num ); //123
    }
    f2();
    console.log( num );//123
}

var num = 456; //全局
f1();
console.log( num );//456

在这里插入图片描述

高级案例1:

1.局部作用域内可以访问全局变量, 即全局变量在哪都能访问,而私有变量只能在当前作用域内访问,出了 
当前作用域则 六亲不认,无法访问报错.

2.但是对于作用域链要注意: 作用域链,子链内部的变量如果是无声明的,则在上一级父作用域链找到声明则是属于父作用域链的局部变量. 如果声明了,则也是各管各的不能在外部去访问私有变量.
//作用域链高级案例:
    function a() {
      var x = 10;
    }

    function c() {
      var x = 20; 
      function b() {
      x = 30;  //b函数未声明x变量,找上一级是c函数的x局部变量
      }
      b(); 
      a(); //a函数的作用域x私有变量不会影响到c函数.
      console.log(x); //30 
    }

    c(); 

高级案例2:


    function a() {
      var x = 10;
    

    function c() {
      var x = 20;
      function b() {
      x = 30;
      }
      b(); 
      
      console.log(x); //30    
    }

    c(); 

  }  
  a();

高级案例3:

 function a() {
      var x = 10;
    }

    function c() {
      var x = 20;
      function b() {
      var x = 30;
      }
      b(); 
      a();
      console.log(x); //20   这里的b和a的作用域的私有变量无法影响到c的局部变量输出
    }

    c(); 

高级案例4:

function a(nms) {
      console.log(arguments);
      //Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
      //var nms
      //fun b 

      // nms = 100;
      // console.log(nms);
      function b() {
        var w = 10;
        console.log(nms);
        nms = 20; //a作用域的a
      }
    }

    a(1, 2, 3); //

量.
//作用域链高级案例:
function a() {
var x = 10;
}

function c() {
  var x = 20; 
  function b() {
  x = 30;  //b函数未声明x变量,找上一级是c函数的x局部变量
  }
  b(); 
  a(); //a函数的作用域x私有变量不会影响到c函数.
  console.log(x); //30 
}

c(); 

### 高级案例2:

```js

    function a() {
      var x = 10;
    

    function c() {
      var x = 20;
      function b() {
      x = 30;
      }
      b(); 
      
      console.log(x); //30    
    }

    c(); 

  }  
  a();

高级案例3:

 function a() {
      var x = 10;
    }

    function c() {
      var x = 20;
      function b() {
      var x = 30;
      }
      b(); 
      a();
      console.log(x); //20   这里的b和a的作用域的私有变量无法影响到c的局部变量输出
    }

    c(); 

高级案例4:

function a(nms) {
      console.log(arguments);
      //Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
      //var nms
      //fun b 

      // nms = 100;
      // console.log(nms);
      function b() {
        var w = 10;
        console.log(nms);
        nms = 20; //a作用域的a
      }
    }

    a(1, 2, 3); //

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值