你不知道的javaScript(作用域和闭包)--作用域是什么?

作用域:储存变量并且之后方便找到这些变量的一套规则。(或者说是根据名称查找变量的一套规则)

1、编译原理

1、javaScript是一门编译语言。虽然javaScript引擎编译语言的步骤和传统的编译语言很相似,但是,与传统编译语言并不同。

2、javaScript并不是提前编译,大部分情况的编译发生在代码执行前的几微妙。或者说编译完立即执行。

3、传统编译语言的编译步骤(然而javaScript比这个复杂的多)

(1)分词/词法分析:将var a=2;这种字符串分解成 var、 a、 =、2、这种词法单元

(2)解析/语法分析:将词法单元数组转换为抽象语法树(AST)

(3)代码生成:AST转化为可执行代码(此过程和语言平台 有关)

2、理解作用域

1、引擎是什么?:从头到尾负责javaScript程序的编译和执行过程。

2、编译器是什么?负责语法分析和代码生成的过程。

2.1 变量赋值的过程

1、对于var a=2;这个变量声明和赋值的语句在引擎看来是两个不同的过程:编译时声明变量运行时给变量赋值

(1)编译器在编译时的处理:编译器在代码生成的过程问作用域:是否存在和a一样名称的变量存在于作用域的集合中?是的话,编译器就忽略了a的声明,继续编译,否的话要求作用域在当前的作用域集合中去声明一个新的变量a。

(2)引擎在运行时的处理;引擎问作用域,当前作用域中有没有a这个变量,有的话拿来赋值2,没有的话就继续找,找到的话赋值2,没有的话抛出异常!

2.2 编译器有话说

1、上一小节我们说引擎在执行的时候会询问作用域有没有a这个变量,在实际的过程当中,是引擎在作用域的协助下通过LHS和RHS方法去查询a

2、什么是LHS和RHS查询?L是左,R为右,当变量出现在赋值操作的左侧进行LHS查询,出现在右侧使用RHS查询。

3、但是并不能完完全全通过左右的方式去判断查询方式,更合理的方式是通过判断,赋值操作的目标是谁(LHS)谁是赋值操作的源头(RHS)来判断查询方式。

4、更容易的理解的判断方式是:判断此处需要的是变量的值(RHS)还是需要变量所在的容器(LHS),比如说

(1)console.log(a) :这个就很明显是RHS引用,因为没有给a赋值,而是需要a的值。或者说a是整个语句赋值操作的源头

(2)a=2:很明显这个是LHS引用,因为这里需要a的容器,或者说a是=2这个赋值操作的目标。

5、举例:

function foo(a){
    var b=a;
    return a+b;
}

var c=foo(2);

这个里面有3处LHS查询:(1)c=foo(2) ; (2)a=2(隐式变量分配); (3)b=a

这个里面有4处RHS查询:(1)b=a;(2)a+ ...;(3)...+b;  (4)foo(2)(函数的调用要对函数进行RHS引用)

2.3 作用域嵌套

1、在当前作用域无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,知道找到整个变量或者抵达最外成的作用域

2、举例:

function foo(a){
    console.log(a+b)
}
var b=2;
foo(2);

引擎:foo的作用域兄弟,你见过b么,我要对它进行RHS引用!

foo的作用域(当前作用域):没见过,滚!

引擎:foo的上级作用域兄弟(全局作用域),你见过b么,我要对它进行RHS引用!

全局作用域:当然见过,给你!

3、所以通过上面这个例子你会明白两个问题。第一就是引擎在当前作用域查询变量的时候,没有查到并不会停止的原因,因为还要到外层作用域继续查,查到底。第二就是,javaScript变量的声明写在什么文件头部尾部都不重要,只要它能在使用它的作用域或者外层作用域就行了,即引擎在对其进行RHS引用能在最终找到它

2.4异常

1、为什么要区分LHS和RHS?因为在RHS在所有嵌套的作用域中都找不到所需的变量,引擎就会爆出ReferenceError的异常但是LHS查询找不到目标标量,就会在全局作用域中去创建一个具有该名称的变量,前提是在非‘’严格模式‘下因为在严格模式下是禁止自动或者隐式的创建全局变量

2、TypeError:对变量的值进行不合理的操作,引擎就会抛出TypeError的错误。

3、ReferenceError是同作用域判别失败相关,而TypeError是作用域判别成功,但是对结果的操作非法或者不合理

猜你喜欢

转载自blog.csdn.net/weixin_37968345/article/details/81324296