1.作用域是什么,它拿来做什么用?
引用你不知道的JS中的概念。
首先几乎所有语言的最基本功能之一就是存储变量中的值,并且能在之后对这个值进行访问或修改。那么这些变量存储在哪里,程序需要时如何找到他们。
这些问题说明需要一套设计良好的原则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域。
这里引入一个题外话,程序的源代码在执行之前会经历三个步骤1.分词/词法分析2.解析/语法分析3.代码生成,统称为“编译
2.变量的赋值操作发生了什么?
变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后再运行时JavaScript引擎会在作用域中查找该变量,如果能找到就会对它赋值。
其中JavaScript引擎(后称引擎)是执行怎么样的查找会影响最终的查找结果
以var a = 2为例
引擎会为变量a进行LHS查询。另外一个查找的类型叫做RHS
L对应Left,R对应Right。
当变量出现在赋值操作的左侧时进行LHS查询,出现在右侧时进行RHS查询。
更准确的话,RHS查询与简单地查找某个变量的值别无二致,而LHS查询则是试图找到变量的容器本身,从而对其赋值。从这个角度说,RHS并不是真正意义上的“赋值操作的右侧“而是”非左侧", 可以理解为retrieve his source value(取到它源头的值),RHS查询是对赋值操作源的查询
RHS的代码解读
console.log(a);
其中对a的引用是一个RHS引用,因为这里a并没有赋予任何值。相应地,需要查找并取出a的值,这样才能将值传递给console.log();
LHS的代码解读
a = 3;
这里对a的引用则是LHS引用,因为实际上我们并不关心当前的值是什么,只是想要为=2这个赋值操作找到一个目标。
复杂一点的栗子
function foo(a){
console.log(a);//2
}
foo(2);
最后一行foo(...)函数的调用需要对foo进行RHS引用,意味着”找到foo源头的值并返回给我“,且(...)意味着foo的值需要被执行。
代码中对foo参数a的隐式赋值之前需要进行LHS查询
console.log(a)需要对a进行RHS引用,并且将得到的值传给了console.log(...)
console是一个对象在调用其方法之前需要进行RHS查询,
查询到log对象后在log方法中对log(args1,args1,...)的第一个参数赋值之前进行LHS查询
3。作用域的嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,知道找到该变量或者抵达全局作用域为止(已在全局作用域中查找)。
看下面一段代码
function foo(a){
console.log(a+b);
}
var b = 2;
foo(2);//4
可以看出对console.log(a+b);中
对b的RHS引用无法在函数foo内部完成,但可以在上一级作用域中完成‘(这里是全局作用域)
遍历嵌套作用域链的规则:
引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。(查找停止在全局作用域)
4.那么前面说的RHS和LHS有什么用呢???你是不是偏题了???喵喵喵?
因为在变量还没有声明的情况下,这两种查询的行为是不一样的
实例代码
function foo(a) {
console.log(a+b);//Uncaught ReferenceError: b is not defined
b = a;
}
foo(2);
console.log(a+b)中对b进行RHS查询时时无法找到该变量的,也就说这是一个未声明的变量,因为在任何相关的作用域中都无法找到它
如果RHS查询在所有嵌套的作用域中遍寻不大所需的变量,就会抛出ReferenceError异常
相较之下在非严格模式下当引擎执行LHS查询(b = a;中的b =),如果在顶层中也无法找到目标变量全局作用域中就会创建一个具有该名称的变量
并将其返回给引擎。(严格模式下会抛出ReferenceError),RHS查询到一个变量,但是你尝试对这个变量的值进行不合理的操作时候,就会抛出TypeError(这个大家很熟悉)
ReferenceError和作用域判别失败有关,而TypeError则代表作用域判别成功,但是对结果的操作是非法或不合理的。
小结
作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用LHS查询,如果目的是获取变量的值,就会使用RHS查询。赋值操作符会导致LHS查询。(b = a既有LHS查询又有RHS查询) =操作符或调用函数时传入参数的操作都会导致关联作用域的赋值操作。
js引擎首先会在代码执行之前对其进行编译,在这个过程中,像var a = 2 这样的声明会被分解成两个独立的步骤
1.首先,var a 在其作用域中声明新变量。这会在最开始的阶段,也就是代码执行前进行。
2.接下来,a = 2会查询(LHS)变量a并对其赋值。
LHS查询和RHS查询都会从当前作用域像上层作用域执行。
不成功的RHS 抛出ReferenceError
不成功的LHS 抛出ReferenceError(严格模式)
创建一个LHS引用的目标作为标识符的全局变量(非严格模式)
匿了3个月,再次系统的学习了js,做了三个项目,有不少体会,后面会逐一分享,谢谢大家的观看,如若有误欢迎指出,感激不尽~