js运行三部曲
1.语义分析
2.预编译
3.语法执行
# 预编译
# 函数声明整体提升
function test() {
return 1;
}
//无论test写在前面还是写在后面 都可以正常输出
//因为预编译把函数声明往往放在了第一位
test();
1
2
3
4
5
6
2
3
4
5
6
# 变量声明提升
console.log(a); // 1
//因为声明了变量a var a被提升到 consloe.log 前面 打印undefined
//未声明变量 会报错 未定义
var a = 1;
1
2
3
4
2
3
4
var a = 123; //声明变量a 并给a赋值123
var a = 123; //拆分就是 var a; a = 123;
1
2
2
document.write(a); //undefined
var a = 123;
//因为把声明变量a提到前面去了
1
2
3
2
3
# 暗示全局变量
imply global 一切声明的全局变量,全归window所有
# 例子
function test(){
var a = b = 123;
//b未经声明被123赋值此时就归 window 所有
//window.b 值为123;
}
test();
1
2
3
4
5
6
2
3
4
5
6
//window就是全局的域
var b = 234;
//window就像一个仓库一样(电脑仓库)
var a = 123;
window {
a : 123;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 例子
function test() {
var b = 123;
}
test();
console.log(window.b); //打印undefined
//因为b是局部变量
//一切声明全局变量的 都是window的属性 b不是全局变量
1
2
3
4
5
6
7
2
3
4
5
6
7
//window就是全局
var a = 123;
console.log(a); ---> console.log(window.a);
1
2
3
2
3
# 局部预编译过程
预编译发生在函数执行的前一刻
局部预编译过程
1.创建AO对象 Activation Object (执行期上下文)
2.找形参和变量声明,将变量和形参名作为AO对象属性名,值为undefined
3.将实参值和形参统一
4.在函数体里面找函数声明。值赋予函数体
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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
第一步: 创建AO对象
第二步:AO {
a :undefined, //形参
b :undefined, //变量声明
}
第三步:AO {
a :1, //形参和实参值相统一
b :undefined,
}
在函数体里面找函数声明function a (){},
把它的函数体赋给AO对象的属性(此时又找到了函数声明 d)
值赋予函数体
第四步:AO {
a :function a() {},
b :function () {},
d :function d () {}
}
执行函数开始 (从上到下依次执行 对照着AO对象里面执行)
a = 123 ; //提升完的变量就不用管了(var a)
AO {
a :123;
b :function () {},
d :function d () {}
}
//打印结果为 function 123 123 function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 例子
function test(a, b) {
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b() { }
function d() { }
console.log(b);
}
test(1);
AO {
a : 3
b : 2
c : 0
d : function d() {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 例子
function test(a, b) {
console.log(a); //fn
console.log(b); //undefined
var b = 234;
console.log(b); //234
a = 123;
console.log(a); //123
function a() { }
var a;
b = 234;
var b = function () { }
console.log(a); //123
console.log(b); //fn
}
test(1);
AO {
a : 123,
b : function () {},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
小规律
一旦有a变量 a函数重名 第一条输出的还是a 那么一定是函数a 因为预编译的第四部值赋予函数体
预编译不仅发生在函数体 还发生在全局
# 全局预编译过程
全局预编译过程
没有了函数预编译的第三步,
只需要创建GO对象找形参和变量声明和函数声明然后赋值
# 例子
console.log(a); //undefined 因为var a提升上去了 没有赋值
var a = 123 ;
//全局里面放了函数声明 将函数也提升上去
function a() {
}
GO {
a :123,
}
GO ==== >window
console.log(window.a);
console.log(a); //两个相等
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 例子:
function test() {
var a = b = 123;
console.log(window.b); //输出123
console.log(window.a); //输出undefined 因为a在AO里不在window
}
AO{
a: undefined--->123
}
GO{
b:123
}
//因为b未经声明就赋值的话 归全局window(GO)所有
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 例子:
console.log(test);
function test(test) {
console.log(test); //fn
var test = 234;
console.log(test); //234
function test() {
}
}
test(1);
var test=123;
步骤:
创建GO对象 GO{
test: function() {}
}
执行函数 执行的前一刻进行预编译(执行上下文)
创建AO对象 AO{
test: function() {}
}
//AO和GO里面同时有 先找AO的 AO里面没有 再找GO(找近处的来)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 例子:
var global = 100;
function fn() {
console.log(global);
}
fn(); //100
Go {
global: 100
fn: function fn() {}
}
AO {
}
//AO里面没有 去GO里面找 输出100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 例子:
a = 100;
function demo(e) {
function e() { }
arguments[0] = 2;
console.log(e);
if (a) {
var b = 123;
function c() {
}
}
var c;
a = 10;
var a;
console.log(b);
f = 123;
console.log(c);
console.log(a);
}
var a;
demo(1);
console.log(a);
console.log(f);
步骤:
GO {
a: 100,
demo : function () { }
}
AO {
e: 1, 2,
b: function e() { }, undefined,
c: undefined, function () { }
a: undefined; 10
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 练习题
var str = false + 1;
document.write(str);
//相加隐式类型转换为number false转换为number为0 打印1
1
2
3
2
3
var demo = false == 1;
document.write(demo);
//判断false是不是等于1 不等于 输出布尔值false
1
2
3
2
3
if (typeof (a) && -true + (+undefined) + "") {
// ”undefined“ && "NAN"
document.write('基础扎实');
}
1
2
3
4
2
3
4
if (11 + "11" * 2 == 33) {
//乘法两边都要转换成数字 所以字符串11=11
document.write("基础扎实")
}
1
2
3
4
2
3
4
undefined bar
(window.foo || (window.foo = 'bar'));
//先看后面的括号里面的
//window.foo的值'bar'
1
2
3
4
2
3
4
# 总结例子
a = 100;
function demo(e) {
function e() { }
arguments[0] = 2;
//arguments[0]代表第一位实参
//arguments[0]改其实e就改
document.write(e);//2
if (a) {
var b = 123;
function c() {
}
}
var c;
a = 10;
var a;
document.write(b); //undefined
f = 123;
document.write(c);
//理应是fn因为语言规定不能给if语句里面写函数所以是undefined
document.write(a); //10
}
var a;
demo(1);
document.write(a); //外部GO里面a100
document.write(f); //123
第一步:AO {
e: undefined
b: undefined
c: undefined
a: undefined
}
第二步:AO {
e: 1
b: undefined
c: undefined
a: undefined
}
第三四步:AO {
e: 2
b: undefined
c: function c() { }
a: 10
}
//因为f在函数体AO里面没有,所以就暗示全局变量window,因为window就是GO所以
GO{
f: 123
a: 100
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48