作用域是什么

412 阅读5分钟

[TOC]

什么是作用域

作用域是一套用于确定在何处以及如何查找变量的规则。

变量的赋值操作过程(先声明,再赋值)

var a = 2
  1. 遇到var a 编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的 集合中。如果是,编译器会忽略该声明,继续进行编译,只是根据上下位置决定是否重新赋值;
   var a = 2;
   var a = 3;
   console.log(a);//3

否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。

  1. a = 2 引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作a的 变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。 如果引擎最终找到了a变量,就会将2赋值给它。否则引擎就会举手示意并抛出一个异 常

  2. function num() {
    	b = 5;
    }
    num();
    console.log(b);//5
    

    如果函数中没有使用var,直接给b赋值,先在函数中找是否存在相同变量,然后往上一级作用域找,直到全局作用域中,如果有,那么就相当于给已经存在的变量重新赋值(声明变量提升);如果没有,那么就创造一个全局变量b(也就是window.b)。

  3. 测试题

    function num() {
    	var a = b = 5
    }
    console.log(b); //Uncaught ReferenceError: b is not defined 作用域判别失败
    

    上方中没有执行函数,也就没有进行赋值操作,所以b显示undefined。

    (function () {
    	var a = b = 5;
    })()
    console.log(b);// 5
    

    上方为立即执行函数表达式,函数内进行了赋值操作,编译器询问是否有b变量存在,发现函数内与函数外都没有b变量,又因为前方的var被a所使用,所以创造一个全局变量b,然后给这个变量5赋值为5。

    function num() {
    	"use strict"
    	var a = b = 5
    }
    num()
    console.log(b);//Uncaught ReferenceError: b is not defined  
    

    严格模式会禁止自动或隐式地创建全局变量

    function num() {
    	"use strict"
    	var a = window.b = 5
    }
    num()
    console.log(b);//5 只有主动创建全局变量才能行
    

LHS查询和RHS查询

  • 引擎执行代码时,会通过查找变量来判断是否声明过,而查找的方式会影响最终的查找结果
  • 查找目的是对变量进行赋值,使用LHS查询;查找目的是获取变量的值,使用RHS查询
  • 注意函数参数的隐式赋值(LHS)

例子1

例子2

作用域嵌套规则

  • 什么是作用域的嵌套:当一个块或函数嵌套在另一个块或函数中时。
  • 遍历嵌套作用域链的规则:引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都

异常

ReferenceError
  1. 作用域判别失败
  2. RHS 查询在所有嵌套的作用域中遍寻不到所需的变量
  3. 严格模式中 LHS 查询失败时(赋值失败),并不会创建并返回一个全局变量,引擎会抛出同 RHS 查询 失败时类似的ReferenceError异常
TypeError
  1. 作用域判别成功了,但是对结果的操作是非法或不合理的
  2. RHS 查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作
  3. 试图对一个非函数类型的值进行函数调用,或着引用null或undefined类型的值中的 属性

考题

考题1:

var arr = [1, 2, 3];
    for (var i = 0, j; j = arr[i++];) {
        console.log(j);
    }

    console.log(i);

    console.log(j);

解答:

因为JavaScript没有语句作用域,所以var i = 0,相当于是全局变量。j也是全局变量。

i++ 是在 i 使用后再自加:
第一次执行时,j=arr[0],之后 i=1,console.log(j) 输出 1 
第二次执行时,j=arr[1],之后 i=2,ocnsole.log(j) 输出 2
第三次执行时,j=arr[2],之后 i=3,ocnsole.log(j) 输出 3
第四次(不符合 for  条件),j=arr[3] 为 undefined,之后 i=4,ocnsole.log(j) 没有输出,退出 for 循环

for 语句执行结束后,console.log(i) 由上分析可知输出 4,console.log(j) 输出 undefined

考点:

  1. 如何形成全局作用域与局部作用域

    • 全局变量是指在任何地方都可以访问的变量,有两种情况

      ​ 在 function 外面声明,不论是否用 var 关键字

      ​ 在 function 里面声明,不使用 var 关键字,当然声明的语句必须被执行才可以

    • 局部变量只能在被声明的 function 内部才能访问

      ​ 在 function 里面声明,使用 var 关键字

  2. JavaScript没有语句作用域

     alert(i); // 输出 undefined
    
     for (var i = 0; i < 1; i++){};
    
     alert(i); // 输出1
    //在语句内定义的变量会扩散到语句外边, 例子中 i 在 for 语句中声明,但是在 for 语句的外面任然可以访问
    //在 for 语句之前就可以访问到 i ,只不过这时候还没有被赋值(var i提升到其作用域的最顶部,因为i是全局作用域,所以alert(i)也可以使用)
    
  3. i++跟++i的区别(i++是自身使用后,再加1)

    i++:先赋值,再自增

    ++i:先自增,再赋值

改题1:

var arr = [1, 2, 3];
    for (var i = 0, j; j = arr[++i];) {
        console.log(j);
    }

console.log('---------');
    console.log(i);
console.log('---------');
    console.log(j);
console.log('---------');

解答:

++i 是在 i自增后再赋值:
第一次执行时,i=1,j=arr[1],console.log(j) 输出 2
第二次执行时,i=2,j=arr[2],ocnsole.log(j) 输出 3
第三次执行时,i=3,j=arr[3],ocnsole.log(j) 输出 undefined,退出for循环

for 语句执行结束后,console.log(i) 由上分析可知输出 3,console.log(j) 输出 undefined

参考链接www.cnblogs.com/shinejaie/p…

考题2

参考链接www.cnblogs.com/hfxm/p/5547…

考题3

参考链接www.jianshu.com/p/94ee757ca…

考题4

参考链接juejin.cn/post/684490…

考题5

参考链接segmentfault.com/a/119000001…