学完这一章最大的感触就是可以弄懂一些平时一直在遇到但是或许并不清楚原理
的知识点,比如下面两个问题,你清楚原理吗?
Q1:你知道为什么在一个作用域内不使用var声明的变量会变成全局变量吗?
Q2:你知道报错信息
referenceError
和TypeError
有什么区别吗?它们分别在什么情况下会产生?
《你不知道的javascript》(上卷)
中对于作用域的定义是:
作用域是根据名称查找变量的一套规则。
而我在《javascript高级程序设计》
一书中写的笔记是:
作用域是变量和函数的可访问范围。
我认为这两者并不矛盾,作用域是一套规则,因为这套规则,所以确定了变量和函数的可访问范围。
首先理解一下变量赋值(eg:var a = 2)
会执行的两个操作:
-
编译器
在当前作用域中声明一个变量(如果之前没有声明过) - 运行时
引擎
会在作用域中查找该变量,如果能找到就会对它赋值。
其中引擎
在作用域中查找该变量的时候有两种查询方式:LHS
和RHS
LHS查询
:
当变量出现在赋值操作符的左边的时候会执行LHS
查询,例如a=2
,对于a
的查询就是LHS
查询
换句话说,如果查找的目的是对变量进行赋值
,那么就会使用LHS
查询。
RHS查询
:
当变量出现在赋值操作符的右边(严格来说是非左边)时会执行RHS
查询,例如console.log(a)
,这里我们需要查询到a
的值,但a
的值并没有在操作符的左侧,所以我们对变量a
执行的是RHS
查询
换句话说,如果查找的目的是获取变量的值
,就会使用RHS
查询
下面用一个题目来考察是否理解清楚了LHS
和RHS
查询:(题目来自《你不知道的javascript》(上卷))
function foo(a){
var b=a;
return a+b;
}
var c=foo(2)
提示:LHS
查询有3
处,RHS
查询有4
处噢
答案:
c=foo(2)
;对c
进行LHS
查询
a=2
(隐式变量分配);对a
进LHS
查询
b=a
;对b
进行LHS
查询
foo(2)
;对foo
进行RHS
查询
b=a
;对a
进行RHS
查询
a+b
;对a
和b
都进行RHS
查询(因为查询的目的是获取a
和b
的值)
你答对了吗?
弄清楚LHS
和RHS
我们可以开始讨论Q1
和Q2
了:
如果RHS
查询在所有嵌套作用域中查询不到所需要的变量
,就会抛出referenceError
的异常,如果RHS
查询找到了
一个变量,但是试图对这个变量做不合理或者非法的操作
,就会抛出TypeError
的异常。
LHS
查询在所有嵌套作用域查询不到所需要的变量,就会在全局作用域中创建一个具有该名称的变量
,并将其返还给引擎
(非严格模式下),严格模式
下也会抛出一个类似的referenceError
错误。所以在一个局部作用域中不用var
声明变量的时候,语法就类似于a=2
,此时引擎对变量a
的查询是LHS
查询,所以在非严格模式
下会在全局变量中以该名称创建一个变量。