讲预编译之前,首先我们先了解JavaScript运行的特点
你可能立马想到这几个词:单线程?解释型语言?翻译一句执行一句,其实还没有这么直观,读一句执行一句是到最后一步才去弄的。在JavaScript执行之前,分为三步:
第一步:语法分析(或语义分析)
系统会扫描一遍,看你有没有语法错误,少个花括号,中文逗号等等。这个通篇扫描的过程就叫做语法分析。通篇扫描完成之后就会进行第二步
第二步:预编译
第三步:解释执行
在讲预编译之前,我们先铺垫一点东西,请看下面代码:
function test() {
console.log('a');
}
test();
能不能执行?当然能啊,打印输出 'a',那么再请看下面代码:
test();
function test() {
console.log('a');
}
你可能会想,读一行执行一行,应该不会输出,但是由于预编译的存在,还是打印 'a'
var a = 123;
console.log(a);//此时输出 123
如果是下面这样呢?
console.log(a);//此时输出 undefined
var a = 123;
或者是这样?
console.log(a); // 变量没声明,当然报错啊
// var a = 123;
但是为什么在定义变量a赋值之前输出就是undefined的呢?
我们有两句话:
1.函数声明整体提升
2.变量 声明提升
首先来理解第一句:
不管在哪里写的函数声明,系统总是会把这个函数提升到逻辑的最前面,不管你是在函数声明的上面调用还是函数声明的下面调用,其本质都是在函数声明的下面调用。
现在来理解第二句:
变量 声明提升 我中间加了空格代表有意思的
var a = 123;
上面的操作是定义变量再加变量赋值,是不是两步?但是这么来写的话就相当于变量声明提升了
console.log(a);
var a = 123;
所以上面的代码相当如下
var a;//此时会被丢到程序的最前面
console.log(a);// undefined
a = 123;
讲到这里先记住上面总结的简单两句话:
1.函数声明整体提升
2.变量 声明提升
但是呢?这两句话太过于肤浅。不能只靠这两句话啊!看下面代码
console.log(a);
function a () {}
var a = 123;
我声明了一个函数a,又var定义一个变量赋值123,我在最上面输出的a是什么?
我在变一下
console.log(a);
function a(a) {
var a = 234;
var a = function() {}
a();
}
var a = 123;
此时函数的参数也是a,里面也搞个var a = 234,又来个函数表达式 a 并且再下面调用,我现在问你怎么办?学无止尽,上面的只是带你热身,现在老衲继续深入探讨
预编译前奏
1.imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有
2.一切声明的全局变量,全是window的属性
console.log(a);
执行上面操作,报错,因为根本就没有a这个变量
a = 123;
如果我这样写呢?当然不报错,也能获取 a 的值,但是 这样的写法 和 var声明的变量a也是有区别的。**任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。**这个全局对象是谁呢?是window。这个window很有意思
a = 10; -----> window.a = 10任何变量,如果变量未经声明就赋值,此变量就为全局对象所有
但是如果你是使用var声明的var b = 234;window上还是有b这个变量的值。因为一切声明的全局变量,全是window的属性
window就是全局的域
var a = 123;就相当于在window上定义了一个属性a
window: {
a:123
}
如果在全局内访问a其实是访问window.a
现在我写了如下代码:
function test() {
var a = b = 123;
}
test();
console.log(window.a);
console.log(window.b);
这个中间过程先把123赋值给未经声明的b,然后再去声明a,把b的值赋值给a,这个过程导致一个问题就是b未经声明就赋值了,那是不是这个b就属于window了?(前面刚写的啊,不准忘),那么此时在全局访问window.a应该是没有,但是访问window.b就应该有值
趁热打铁,赶紧看下面代码:
function test() {
var b = 123;
}
test();
console.log(window.b);这里输出什么?undefined,前面说过一切声明的全局变量是widnow的属性,但是我这个var b = 123; 是在哪里声明的?我是在声明的函数中,局部定义的变量,那就不能归window所有。就记住window就是全局。
好了好了,前面铺垫了这么多终于到了预编译的过程,泪目
预编译
下面举个例子来讲:先想下调用函数里面会输出那些内容
function fn(a) {
console.log(a);
var a = 123;
console.log(a);
function a() {}
console.log(a);
var b = function() {}
console.log(b);
function d() {}
}
fn(1);
我们先搞清楚fn函数中,又有函数声明又有变量声明,肯定是会涉及到覆盖的问题,它们是按照一个什么样的原则覆盖的呢?最骚的是fn函数的参数也来插一脚也是a,这就是涉及到预编译的奇妙过程。
预编译发生在函数执行的前一刻:
1.**创建AO对象(Activation Object)**执行期上下文
AO {
}
**2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined**
找到一个新参的声明a,又找到一个var a ,一样的名字就写一个,还找到函数表达式的声明var b ,此时的AO对象
AO {
a: undefined,
b: undefined
}
**3.将实参和形参统一**
实参是1,现在形参在AO对象上是undefined,值统一,变为1
AO {
a: 1,
b: undefined
}
**4.在函数体里面找函数声明,值赋予函数体**
找到 function a() {} 和 funtion d() {},并把他们的值赋予函数体
那么此时
AO对象中a的值为1 被替换为函数体了。
第四步结束后,开始执行函数,第一个输出的a是什么?AO对象中的a,那么第一次输出的是function,下面 var a = 123,已经在前面发生了预编译,剩下执行的是a = 123,AO对象里的a
也被赋值为123,然后再执行console.log(a);是不是就变成123了?接下来的function a() {}还需要用看吗?不用看了,在预编译的时候就已经提升上去了,接着下面的console.log(a);还是123,var b = function () {}还需要看吗?var b 是不是提升上去了?就剩一个 b = function() {},因此下面打印的b是一个function.因此依次打印的结果就是function,123,123,function
下面自己练习一下 答案去控制台找,如果写的有什么不足的地方欢迎在评论区补充哦。
function test(a, b) {
console.log(a);
console.log(b);
var b = 234;
console.log(b);
a = 123;
console.log(a);
function a() {}
var a;
b = 234;
var b = function() {}
console.log(a);
console.log(b);
}
test(1);