首页 > 代码库 > 作用域是什么?
作用域是什么?
JavaScript作为编程语言,最基本的功能之一就是能够存储变量当中的值。而一套设计良好的用来存储变量,并且之后可以方便地找到这些变量的规则,被称为作用域。
一、编译原理
首先要清楚,任何JavaScript代码片段在执行前都要进行编译,然后做好执行他的准备,并且通常马上就会执行他。其中:
- 引擎:从头到尾负责整个JavaScript程序的编译及执行过程;
- 编译器:负责语法分析及代码生成;
- 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确认当前执行的代码对这些标识符的访问权限。
这三者如何协同工作?
例如 var a = 2;这段程序,一般认为这是一句声明,但引擎却认为这是两个完全不同的声明:一个由编译器在编译时处理,另一个则由引擎在运行时处理。
这个变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对他赋值。
查找过程中,引擎会进行两种类型的查询:LHS查询、RHS查询。
LHS查询:试图找到变量的容器本身,从而可以对其赋值(当变量出现在赋值操作的左侧时进行,查找的目的是对变量进行赋值);
RHS查询:与简单的查找某个变量的值一样(当变量出现在赋值操作的非左侧时进行,目的是获取变量的值)。
二、作用域嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(全局作用域)为止。
三、抛出异常
区分LHS和RHS是一件很重要的事,因为在变量还没有声明的情况下,这两种查询的行为是不一样的。
如果RHS查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError异常;当引擎执行LHS查询时,如果在顶层(全局作用域)中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎(前提是程序运作在非‘严格模式’下)。
严格模式禁止自动或隐式地创建全局变量。
如果RHS查询找到了变量,但是接下来对这个变量的值进行不合理的操作的话,比如试图对要给非函数类型的值进行函数调用,或者引用null或undefined类型的值中的属性,那么引擎会抛出林外一种类型的异常,叫做 TypeError异常。
ReferenceError跟作用于判别失败相关,而TypeError则代表作用域判别成功了,但是对结果的操作是非法或者不合理的。
四、一个小测验
function foo(a){ var b = a; return a + b;}var c = foo(2);
这段程序有几处LHS查询?几处RHS查询?
拓展
JavaScript异常类型
- 1.SyntaxError:解析代码时发生语法错误;
- 2.ReferenceError:引用一个不存在的变量时发生错误; || 将一个值分配给无法分配的对象,比如对函数的运行结果或者this赋值;
- 3.RangeError:当一个值超出有效 范围时发生的错。(数组长度为负数 || Number对象的方法参数超出范围 || 函数堆栈超过最大值);
- 4.TypeError:变量或参数不是预期类型时发生的错误。比如对字符串等原始类型的值使用new命令,就会抛出这种错误;因为new命令的参数应该是一个构造函数;
- 5.URIError:URI相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数;
- 6.EvalError:eval函数没有被正确执行时,会抛出这个错误,已经不再ES5中出现。
上面的六种异常对象都继承自Error对象;关于异常的更多详情,可参考 JavaScript中的异常处理 一文。
严格模式
"use strict";
- 1.全局变量必须显示声明;
- 2.只允许静态绑定;----禁止with语句;创设eval作用域;
- 3.禁止this关键字指向全局对象;
- 4.禁止在函数内部遍历调用栈;
- 5.禁止删除变量;
- 6.显示报错;
- 7.重名错误;----对象不能有重名的属性;函数不能有重名的参数;
- 8.禁止八进制表示法;
- 9.arguments对象的限制;----不允许对arguments对象赋值;arguments不再追踪参数的变化;禁止使用arguments.callee;
- 10.函数声明必须在顶层;
- 11.新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。
严格模式详情可参考阮一峰的 Javascript 严格模式详解 一文。
小测试答案
LHS查询--3处
c = ..; || a (= 2)(隐士变量分配) || b = ..
RHS查询--4处
foo(2.. || = a; || a .. || ..b
作用域是什么?