你不知道的javascript(第一章)

255 阅读4分钟

理解作用域

演员表工作内容
引擎负责整个JavaScript程序的编译及执行过程
编译器负责语法分析及代码生成等
作用域负责收集并维护由所有的声明标识符组成的一系列查询

var a = 2 解析以上演员如何协同工作

1)var a ;编译器会询问作用域是否已经拥有该名称的变量,如果已经拥有该变量,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合声明一个新的变量,并命名为 a。

2)接下来编译器会为引擎生成运行所需的代码,用来处理 a=2 这个赋值操作,引擎也会询问作用域是否有变量a,如果有,引擎使用这个变量将2赋值给她,否则继续寻找,最终如果没有找到变量a引擎便会抛出异常。

小结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能找到就对它赋值。

简单介绍LHS和RHS查询

编译器在编译过程的第二步中生成了代码,引擎执行它时,会通过查找变量a来判断它是否已声明过。查找过程由作用域协作,但是引擎执行怎么样的查找,会影响最终的查找结果。

var a = 2 的例子中 引擎会为a进行LHS查询。(另一个查询叫RHS查询)

LHS、RHS 正如左 右的区别(但不完全是)

换句话说,当变量出现在赋值操作的左侧进行LHS查询,出现在右侧时进行RHS查询。

更准确的说,RHS(retrieve his source value)查询就是查找某个变量的值,而LHS查询则是试图查找变量的容器本身,从而对其赋值(赋值的操作目标)。

考虑下面这列代码

console.log( a );

其中对a的引用就是一个RHS查询,这里需要查询到a的值,这样才能传递给 console.log(......)。

考虑下面的程序的LHS和RHS查询

function foo (a) {
    console.log(a)
}
foo(2)

最后一行foo ( 2 ) 函数的调用 需要对函数进行RHS查询(找到 foo() 的值),另一个查询则是 console.log( a ) 打印a时需要对a进行RHS查询,还会对console对象进行RHS查询,确认其是否有一个log的方法。

在这里大家可能忽略了 a = 2 的隐式操作,这个操作发生在 2 被当成参数传递给函数 foo(a) ,为了给参数a(隐式地)分配值,需要进行LHS查询。

小测验: 找出下列代码片段有几处LHS和RHS查询

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

LHS查询3处:c=... ; a = 2 (隐式变量分配); b = ...

RHS查询4处:foo(2) 函数的调用; b = a ; return a + b (a 和 b 的源值获取)

异常:

如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常,因为在任何相关的作用域都无法找到它(包括全局作用域);

<script type="text/javascript">
	function testError(){
         // ReferenceError: RHS查询
            console.log(a)
	}
	testError()
        // area.html:9 Uncaught ReferenceError: a is not defined
        // at testError (area.html:9)
        // at area.html:11
</script>

当引擎执行LHS查询时,如果在顶层(全局作用域)仍然无法查找到目标变量,全局作用域就会创建一个具有该名称的变量,并将其返回给引擎,前提是程序运行在“非严格模式“下。

<script type="text/javascript">
	// "use strict";
	function leftSearch(){
		a = 3
		console.log(3)
	}
	leftSearch()
	// 懒惰模式下
	// 控制台输出:3 
	// LHS查询未找到变量a,作用域隐式创建变量a
</script>

ES5中引入了”严格模式“,同正常模式(懒惰模式)相比,严格模式在行为上有很多不同,其中一个不同的行为是严格模式禁止自动或隐式地创建全局变量。因此在严格模式中LHS查询失败时,并不会创建并返回一个全局变量,引擎会抛出同RHS查询失败的ReferenceError异常。

<script type="text/javascript">
	"use strict";//严格模式下的程序
	function leftSearch(){
		a = 3
		console.log(3)
	}
	leftSearch()
	// left.html:10 Uncaught ReferenceError: a is not defined
        // at leftSearch (left.html:10)
        // at left.html:13
</script>

如果RHS查询找到了一个变量,但你如果尝试对这个变量的值进行不合理的操作(比如对一个非函数类型的值进行函数调用,或者引用null或undefined类型的值中的属性,那么引擎会抛出另一种类型异常:TypeError。

<script type="text/javascript">
	function testError(){
		// TypeError: RHS查询成功
		var a = 2;
		console.log(a())
	}
	testError()
	// area.html:11 Uncaught TypeError: a is not a function
        // at testError (area.html:11)
        // at area.html:13
</script>

ReferenceError同作用域判别失败相关;TypeError则代表作用域判别成功了,但是对结果的操作是不合理的。


以上则是第一章的主要内容,个人认为这本书主要讲的都是大家容易忽略的一些基础知识和JavaScript的难点。后续还会持续更新余下章节。余下章节有更多的干货(this全面解析、原型链等)、如果有需要《你不知道的javascript》pdf版本的可以私聊我。以上内容均为个人理解,如果有误请指正。