什么是作用域
作用域是指源程序中定义变量的区域。
作用域(可以理解为通过标识符名称访问变量的一组规则)决定了变量该如何被访问,也就是决定了当前的执行 代码对变量的访问权限。
理解作用域需要理解的编译器理论
传统编程语言中执行代码之前都需要经过编译过程。编译分为三个阶段:分词/词法分析、解析/词法分析、代码生成。具体执行过程如下:
分词/词法分析:将代码字符串解析成词法单元
解析/词法分析:将词法单元解析成ast语法树
代码生成:将ast语法树生成机器可以理解的指令,也就是可以执行的代码。
关于编辑器和Js引擎
编译器将代码编译生成可执行的代码(将高级语言编译成机器可以理解的指令),Js引擎来运行这些可执行的代码。
Js和传统编程语言的区别
Js是解释性语言,和传统编程语言一样也有编译过程,只不过Js语言是在执行代码前的很短时间(几 微秒甚至更少)内完成编译过程。
在编译过程中,Js和其他传统语言不同的是,在编译过程中会有性能优化的一个过程。
Js引擎、编辑器、作用域的关系
Js引擎负责整个编译和执行的过程, 编译器负责词法分析和代码生成,作用域负责存储和维护变量,决定当前执行代码对变量的访问权限。
当编译器对代码进行词法分析时,当遇到一个变量首先会去询问作用域,是否已经存在该变量,如果已经存在就忽略声明继续编译,否则就去让作用域集合去创建一个新的变量。
Js引擎在运行代码时,当遇到变量时会进行LHS或RHS查询,询问作用域集合是否已经声明了该变量,对于没有声明的变量如果变量是RHS查询会报错(ReferenceError),LHS查询会在全局作用域中创建新的变量。这也就是说:
c=2;
console.log(c)//2 c是LHS查询
console.log(b)//ReferenceError b是RHS查询
<br /> 这也是为什么说,Js在创建执行上下文时会分2个阶段:创建阶段和执行阶段;创建阶段(也就是编译阶段)会将var 和 function声明的变量优先分配存储空间的原因(这里应该是js编译器的优化过程?不然怎么在声明之前就可以访问,var和function有变量提升的问题?)。在编译器编译代码阶段就会将声明的变量在作用域集合中的去查找比较,作用域中没有就会在作用域中创建。
LHS查询和RHS查询
分别表示左手边查询和右手边查询。
例如:
1、操作符 = ,左边的会进行LHS查询,右边的会进行RHS查询;
2、会函数传参时,申明的函数中的参数会进行LHS查询;
3、当直接输出变量值时,这个变量会进行RHS查询。
4、当函数return 值时,又会有一个RHS查询。
5、当直接调用函数时,会进行RHS查询函数变量。
静态(词法)作用域和动态作用域
Js中采用词法作用域,也就是当函数定义的时候,就创建了作用域。
动态作用域指的是,当函数被调用时才创建作用域(其实js中没有动态作用域,只是this机制在某种程度上很像 动态作用域)。
动态作用域和静态作用域的区别:
一个在运行时确定,一个在定义的时候确定。
思考两个栗子:
栗子1:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
<br />栗子2:<br />两者的结果都是local scope,然而两者的区别是什么?
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
<br />**作用域链**<br /> 多个执行上下文的变量对象组成的作用域链表就构成了作用域链(基于调用栈)。<br /> 函数在被创建的时候,就会创建一个内部属性[[scope]],它里面会包含所有父环境中的变量对象到其中。它其实就是一个作用域列表,里面包含了指向变量对象的指针。<br /> 这也就是所说的函数在被定义的时候就创建了作用域。<br /> 在执行代码的最初阶段作用域链中只包含了全局变量对象,当创建函数的时候,函数中包含了一个scope属性,作用域链会被保存到该scope属性中,当执行函数时会创建执行上下文,然后将执行上下文压入执行上文栈。<br /> 执行执行环境分为进入执行上下文和执行两个阶段,进入执行上下文时,首先会做一个准备工作,首先会复制函数scope属性创建作用域链,然后会将argument、函数参数、变量声明、函数声明加入到活动对象中,然后再将活动对象压入到作用域链的最顶端。准备工作完成,开始执行代码,修改变量对象的属性值。<br /> 那么说的作用域是在函数定义时就创建的,执行函数时活动对象被创建。那么作用域和执行上下文的关系,作用域又与活动对象的关系? 执行上下文与活动对象的关系是什么呢?
未完待续。。。