跨过js大山的第一步-作用域

128 阅读3分钟

前言

本文将记录自己在学习过程中的一些理解,工作中或多或少遇到过作用域问题,但都没有深入了解,希望帮助到有同样经历的朋友,欢迎大家指正沟通交流。

脑图

微信图片_20220512111524.png

什么是作用域?

定义:就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。

  • 那么问题来了,这些变量的值都储存在作用域的那些地方呢?

js预编译

我们都知道,js在运行时会有三个动作,语法检测、预编译、执行代码。 那么预编译会发生什么呢?

        var text = '全局变量';
        function test(a) {
            var private = a;
            function child() {
                // console.log(private);
            }
            child()
        }
        test('局部变量')
        console.log(text);
  • 创建GO对象(Global全局) 微信图片_20220511150737.png
GO = {}
  • 查找变量名做GO对象的属性名,将声明函数的函数名当做GO对象的属性名,值为函数体
GO = {
    text:undefined,
    test:function test() {
            var private = '局部变量';
            function child() {
                // console.log(private);
            }
            child()
        }
}

微信图片_20220511153809.png 从这里我们可以清楚的看到,为什么在声明前打印出undefined的原因,也就是我们常说的声明提前

console.log(text);//undefined
var text='全局变量';

那么函数里的变量又是怎么储存的呢

  • 函数的AO对象
1.创建AO对象:
    AO={}
2.查找函数内的变量名作为AO对象的属性名,值为undefined:
    AO={
        private:undefined
      }
 3.将实参和形参统一,形参做为AO对象的属性名,实参做为值:
     AO={
         private:undefined,
         a:'局部变量'
       }
 4.将声明函数的函数名当做AO对象的属性名,值为函数体:
     AO={
         private:undefined,
         a:'局部变量',
         test:function child() {
                // console.log(private);
            }
      }

好了,讲到这里或许你对作用域还是有点模糊,没关系,看完下面你就明白了

作用域有哪几种

从预编译的过程我们知道,有全局的预编译和函数内部的预编译,实际上就是全局作用域和局部作用域

  • 全局作用域
  • 局部作用域(函数作用域) 先看代码
1. 
var text = '全局变量';
 function test() {
   console.log(text);
   var text='局部变量';
   console.log(text);
   var child='局部变量的弟弟'
  }
 test()
 console.log(text);
 console.log(child);
2. 
var text = '全局变量';
 function test() {
   var child='局部变量的弟弟';
   console.log(text);
  }
 test()
 console.log(text);
 console.log(child);
  1. undefined,局部变量,全局变量,child is not defined
  2. 全局变量,全局变量,child is not defined 代码总结
  • 作用域查找会遵循:如果函数内部AO对象没有该变量,则向GO对象查询
  • 函数外不能访问函数内的变量

先看代码

var val = 1;
function test() { 
    console.log(val); 
} 
function bar() { 
    var val = 2; 
    test();
} 
bar();

//打印1

代码总结

  • 变量声明在定义时就已经确定了其作用域,而不是执行时,静态作用域查找方式

同名变量与同名函数(不建议写同名)

   function test() {
          console.log(child)
          var child = '局部变量';
          console.log(child);
          function child() {};
          console.log(child);
        }
        test();
 
 //function child(){},局部变量,局部变量

块级作用域(ES6新增)

  • 块级作用域:let、const,只能在声明后的{}内访问。
  • 暂时性死区:变量也会变量提升,但是在执行前不会初始化,这个阶段就叫暂时性死区。
    console.log(a);//Cannot access 'a' before initialization
    let a='a变量';
    console.log(a);//a变量
  • let、const声明的变量,除对象的{}外所有的{}都存在块级作用域
    //if
    let isTrue=true;
    if(isTrue){
        let text='{}里的变量'
    };
    console.log(text);//text is not defined
    
    //for循环
    for(let i=0;i<5;i++){}
    console.log(i);//i is not defined

总结

想要搞懂作用域,预编译是必不可少的,代码在执行前我们就可以区分不同作用域,作用域是js的第一座大山,后面很多问题都会用到,最后感谢大家浏览。