学函数的时候,偶然间发现了预编译,就突然很感兴趣,然后也看了许多博客,查了定义。也算是有了些了解,记录下来,当作笔记的开端。
预编译
提到预编译,就不得不说到javascript的运行。
javascript是解释性语言,顾名思义,就是编译一行,执行一行.。
那么在js中,在执行代码之前,还有两个步骤。
1, 语法分析,检查有没有低级的语法错误。
2, 预编译
3, 解释执行 执行代码
####预编译发生在什么时候
预编译发生在script代码块以及函数执行前发生。但是script代码块并没有多少,所以一般都是发生在函数执行前。
做了什么事
当页面产生的时候,script代码块开始执行,这个时候,就发生了预编译。
此时
1, 会创建一个GO全局对象(Global Object) // 也称为执行期上下文
2, 查找变量声明,作为GO属性,值赋予undefined
3, 查找函数声明, 作为GO属性,值赋予函数体
注意 他是按照先找变量声明再找函数声明的顺序来的,所以,如果变量名和函数名一样, 那么会覆盖。
这是发生在script代码块的预编译,发生在函数的预编译呢?
在函数执行之前
同样
1, 首先会创建一个AO全局对象 (Active Object)// 也称为执行期上下文
2, 查找形参和变量声明,值赋予undefined
3, 实参值赋予形参
4, 查找函数声明,值赋予函数体
函数预编译和代码块预编译基本一致,只不过,他们找的变量声明不一样。
凡是属于全局变量的变量声明,都属于GO,凡是属于局部变量的变量声明,都属于这个局部变量所在的函数的AO。任何变量,如果变量未声明就赋值,那么这个变量就属于全局变量,归AO所有。
不过说了这么多,预编译到底可以用来干什么呢?
有什么用
我们来看看这个
var a = true;
foo();
function foo() {
if(a) {
var a = 10;
}
console.log(a);
}
打印的a是什么呢?
在打印之前,我i们不妨用预编译来试着编译一下。
根据预编译定义,我们先找到变量声明a,此时a是undefined,并没有被赋值,然后找到函数foo(),此时foo()是function(){}。
在函数执行前 给函数foo()创建一个AO对象,里面只有一个变量a的声明。为他赋值undefined。注意,预编译定义中,并没有说明任何有关于判断条件,所以,我们 不去管判断到底可不可以被执行,只需要把里面有的声明提生就行了。
然后,函数开始执行,因为变量提升的缘故,函数是先被执行的,此时,a并没有被赋值true,a直接被打印undefined.
预编译的大概就是这样,懂了预编译之后,我们再去看作用域其实就非常比较容易了。
如果声明变量的同时初始化或赋值那么变量优先级高于函数
。
作用域
一般说起作用域,第一反应肯定是全局作用域和局部作用域。
这么说,对是对,但是总感觉,对不起这么高大上的名字。
在javascript中,每个函数都是一个对象,对象中有的属性我们可以去访问他,但是有些是不可以的。
[[scope]]就是一个不能被访问的,仅仅供javascript引擎获取的。他就是我们所说的作用域,其中储存了执行期上下文的集合。这个集合呈链式链接,我们把这种链式链接叫做作用域链。
作用域链
var a = 1;
function f(){
var a = 2;
function f2(){
var a = 3;
function f3(){
console.log(a);
}
f3();
}
f2();
}
f();
这就是一个简单的作用域链
1, 先创建一个GO对象 他拥有变量a 函数f()
2, 在执行函数f() 创建函数f()的AO对象 声明变量a 函数f2()
3, 在执行函数f2() 创建他的AO对象, 声明变量a 函数f3()
4, 此时 要执行函数f3() 但是f3()内部并没有a 他就上他的父级上下文去找,如果富集没有就继续向上找,直到找到为止。
父亲上下文:这个函数在哪个上下文中被声明,那个上下文就是此函数的父亲上下文,这个函数的声明会被保存到父亲上下文的变量对象中。
执行期上下文
函数在执行时,会创建一个名为执行期上下文的内部对象,定义了函数执行时的环境.函数每次执行对应的执行上下文时,都是独一无二的,所以多次调用同一个函数,会创建不同的执行期上戏闻,当函数执行完毕,他所产生的执行期上下文会被销毁。
个人浅见,了解预解析和作用域,对以后学习闭包,平时做题的时候,更得心应手,可以清楚的知道为什么。