javascript预编译部分
JavaScript当中不向其他的语言一样,在js中我们会看到一些奇怪的现象就像我们可以在函数声明之前去访问这个函数的名字,并且能够打印出函数体,而且我们还能在变量声明之前去访问这个变量,虽然值是一个undefined但是并没有报错,这些特殊的情况,归结于函数的预编译过程。
console.log(foo);
function foo(){}
这样的一段代码,根据javascript解释执行的情况,本应该是报错的,因为找不到函数的啊。但是这里的并没有报错,这里打印出来的是整个函数体。同样这样的情况还有我们可以在变量声明之前访问该变量,这种情况被总结为,函数声明整体提升,变量声明声明提升,提升的场所是整个作用域逻辑的最顶端。
两个不一样的全局变量
1. 暗示全局变量(imply global): 当一个变量未经声明就赋值的时候他就被当作是全局变量, 归window所有。
function foo() { var a = b = 123; } foo(); console.log(b); //123
这里的b就成了暗示全局变量,使得我们在函数的外部能够访问到。
2. 一切声明在全局当中的变量都是window对象的属性
var a = 123; console.log(window.a); // 123
它们的区别是,经过声明的全局变量是无法被delete操作符删除,但是未经声明的全局变量能够被delete删除掉。
js执行的三部曲
1. 语法分析 这里是js引擎对全部的代码做一个简单的浏览,检查是否有低级错误。
2. 预编译 发生在函数执行的前一刻,预编译之后会生成相应的GO(global object 全局对象) 和AO(activtion object 活动对象)
3. 解释执行 函数执行期间变量的动态变化即GO/AO两个变量当中的属性值的动态更替。
预编译(函数内部)
1. 生成一个AO对象(activtion object 执行期上下文)
2. 找形参和变量声明,将变量和形参名作为AO对象的属性名,值为undfined
3. 将实参和形参的值相统一,就是将函数的实参赋值给形参
4. 在函数体内部找到函数声明(注意是函数声明,函数表达式情况是不一样的),值赋予函数体
由于是动态变化的,我用一个例子来说明预编译的过程:
函数test:
这里的例子看起来很麻烦,一个名字用在很多地方,很容易将人弄得昏头转向,所以一个合理的规则很重要!!!
1. 生成一个AO对象
AO = { }
2. 找形参和变量声明,将变量名和形参名作为AO对象的属性名,值为undefined
3. 将实参和形参相统一
4. 在函数体内部找到函数声明,值赋予函数体
当我们将这个AO变量分离出来之后,函数就能够说是解释执行了,从上到下,根据这里面的值来动态改变,当然函数声明和变量声明是被提升了。
根据AO对象当中的值可以知道:
第一个log : 这个时候a自然是指的AO当中的a 即 function a() { }
第二个log : 这时的b还没有被赋值操作或者其他的操作,所以也是AO对象当中的b即undefined
第三个log : 这时发生了b = function b() { } 的赋值操作,所以此时的b为function () { } (这里的函数名是忽略的,因为这是函数表达式)
全局的预编译
全局的预编译和局部的预编译相似,只是少了一个将形参和实参值相统一这一步,并且这里的活动对象的名字也变了,叫做Global Object。
函数执行的过程中寻找变量是有顺序的,在函数内部,假如一个变量无法在当前AO中找到就会函数上一层函数的AO当中直到找到GO当中,这也就形成作用域链,接下来的内容当中会详细说明这些过程。