学习ECMAScript时,我们需要跨越三座大山,这三座大山就是:
1. 作用域 & 作用域链
2. 原型 & 原型链
3. 异步 & 单线程
本文是记录一波第一座大山相关的知识点
首先新建一个html文件,准备写js之前,我们会写<script type="text/javascript"></script>,遇到这个标签时,浏览器将会调用js引擎,js引擎首先就开始编译;那么编译都做了些什么呢?创建作用域,即全局作用域和函数作用域
你所理解的作用域是什么?
从狭义上说,作用域是编译过程中JavaScript引擎创建的c++对象;控制变量的可见性和生命周期;从广义上说,作用域就是维护变量查询的一系列规则;
作用域是在何时候产生?
作用域js引擎编译阶段产生,跟函数的声明位置有关,跟函数的调用位置没有关系;对这句话可以通过一段代码的执行来理解
var x = 10;
function fn() {
console.log(x);
}
function show(f) {
var x = 20;
f();
}
show(fn);
此时分析一下x输出的是多少?
分析如下:
- 执行代码至show调用,执行show函数体
- 调用f = fn,执行调用,直接跳到fn声明处进行执行
console.log(x)对x进行右查询,根据作用域链,查找到全局中x = 10;所以输出的是 10
此代码很能说明作用域,跟函数的声明位置有关,跟函数的调用位置没有关系
变量的查询规则
从广义上说,作用域是维护变量查询的一系列规则,那么这个查询的规则是什么?如何判断?
这个查询规则有两种:左查询和右查询
1. 左查询
-
判断准则:变量在等号的左边进行的是左查询
var a = 1; -
规则: 在整条作用域链中都没有查询到这个变量,在全局作用域中主动声明一个
2. 右查询
-
判断准则: 变量在等号的非左边进行的是右查询
-
规则: 在整条作用域链中都没有查询到这个变量时,报错 ReferenceError
那么具体都是些什么?来一段简单的代码分析一下,更便于理解
(function fn() {
function fun(a) {
b = a;
console.log(b); // 2
}
fun(2);
})();
console.log(b); // 2
分析如下:
- js引擎在编译时,先是创建了一个全局的作用域,紧接着马上创建了两个局部作用域
- 在立即执行函数表达式中调用了fun函数,将实参2传给了形参a
- 执行函数体,console.log时,对b进行的是右查询,当前作用域中找,找到了 b = a;这时候对b进行左查询,在整条作用域链中没有找到对b的声明,这时候会在全局中声明一个 b;
- a 在等号的右边进行的是右查询,找到实参 a = 2;然后将2给b(所以此时fun函数体内打印的b是2)
- 在执行全局的
console.log(b)时,由于已经调用过fun,b做查询完主动在全局作用域中声明并且赋过值,所以后面b的输出也是2
总之:
- 作用域只记录着变量的查询规则,不存储变量
- 作用域的个数 = n(不同声明函数声明的个数) + 1
- 作用域分为全局作用域和局部作用域
- ES5中没有块级作用域,只有函数作用域
- 作用域是在编译阶段产生的与函数声明的位置有关与函数的调用位置没有关系