1.检查通篇的语法错误(语法分析)
1.5预编译的过程(预编译)
2.解释一行执行一行(解释执行)
text();
function text() { console.log(1); } // 1
var关键字的声明提升,赋值不提升
var关键字声明的变量会自动提升到函数作用域顶部。
所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。
此外,反复多次使用 var 声明同一个变量也没有问题。
console.log(a) // Uncaught ReferenceError: a is not defined
console.log(a);
var a; // undefined
console.log(a);
var a = 10; // undefined
// 提升的仅仅是变量的声明而不是初始化
undefined和error不一样!!!
一个奇奇怪怪的例子console.log(a);
function a(a) {
var a = 10;
var a = function () {
}
}
var a = 1;
// 打印结果竟然不是undefined!
// ƒ a(a) {
// var a = 10;
// var a = function () {
//
// }
// }
暗示全局变量
预编译有个特点:任何变量,如果未声明就赋值,那该变量为全局变量,即暗示全局变量(imply global)。并且所有的全局变量都是window的属性。
var a = 1;
b = 2;
console.log(window.b); // 1
console.log(window.a); // 2
// var *** 作符在全局作用域下声明的变量自动变为window的对象 如a
// 在函数内定义变量时省略var会创建全局变量 如b
function test() {
var a = b = 2;
}
test();
console.log(b); // 2
console.log(a); // Uncaught ReferenceError: a is not defined
console.log(window.a); // undefined
// 函数体先声明了一个局部变量a,然后不使用var声明了一个全局变量b,将b赋初值为2,最后用b的值初始化a
// 因此b作为全局变量在函数test()外部仍能访问
// a作为局部变量,只有函数作用域
// window对象打印不存在的属性时显示undefined
// 在第二局console报错时不会继续向下执行了,所以注释掉这一句
AO对象
AO对象全称为:activation object (活跃对象/执行期上下文/函数上下文),在函数执行前执行函数预编译,此时会产生一个AO对象,AO对象保存该函数的参数变量。
函数预编译步骤:
产生空的AO对象找形参和变量声明,作为AO对象的属性名,值为undefined。实参值赋值给形参。在函数里面寻找函数声明,函数名作为AO对象的属性名(若形参、变量声明与函数名重叠,则函数体值会覆盖参数值与undefined),值为函数体。执行
// eg.1
function test(a) {
console.log(a);
var a = 1;
console.log(a);
function a() { };
console.log(a);
var b = function () { };
console.log(b);
function d() { };
}
test(2);
创建AO对象
AO{
//此时AO对象为空
}确定AO对象的属性名
e.g.1AO{
a:undefined; //函数参数
b:undefined; //函数里面声明的变量
}将实参值赋值给形参
AO{
a:undefined -> 2 -> //函数参数
b:undefined; //函数里面声明的变量
}处理函数里面的声明函数
AO{
a:undefined -> 2 ->
function a () {} -> //函数声明提前,变量名一样,值覆盖
1,
b:undefined ->
function (){}, //函数里面声明的变量
d:function d () {}
}
此时函数预编译已经完成的,预编译后执行代码:
第一条执行的是控制台打印出a的值,所以输出function a () {};
第二条语句赋值给a,则AO对象中a的值被覆盖为2;
第三条语句控制台打印a的值为1;
第四条为声明,预编译处理过所以直接跳过;
第五条打印出a的值,一样为1;
第六条为赋值,赋值b的值为function () {};
第七条打印出b的值function () {};
第八条声明,预编译处理过所以直接跳过;
所以输出结果应该是
function a () {};
1;
1;
function () {};
执行效果如图:
// eg.2
function test(a, b) {
console.log(a);
c = 0;
var c;
a = 5;
b = 6;
console.log(b);
function b() { };
function d() { };
console.log(b);
}
test(1);
AO = {
a: undefined ->
1 -> // 此时打印第一个console
5,
b: undefined ->
function b() { } ->
6, // 此时打印第二个console,并且由于下面对b()的函数声明
//在预处理时做过了,直接pass,第三个console也输出这个b
c: undefined ->
0,
d: function d() { },
}
GO对象
GO对象全称为 global object(全局对象,等同于window),在开始预编译时产生的对象,比AO对象先产生,用于存放全局变量,也称为全局作用域。
预编译之前进行
生成空的GO对象将变量声明的变量名当做GO对象的属性名,值为undefinded将声明函数的函数名当做GO对象的属性名,值为函数体
console.log(a);
var a = 123;
function a() {
}
console.log(a);
// GO: {
// a: undefined ->
// function a() { } -> // 此时执行第一句console,预编译结束
// 123, //此时执行第二句console
// }
AO和GO综合练习
1.求输出结果
console.log(a); // undefined
a = 100;
function test() {
console.log(a); // undefined
a = 200;
console.log(a); // 200
var a = b = 300;
}
test();
console.log(b); // 300
var a;
console.log(a); //100
GO: {
a: undefined ->
100,
function test() { },
b: undefined ->
300,
}
AO: {
a: undefined ->
200 ->
300,
}
生成GO对象
GO{
}将声明变量添加进GO对象内,值为undefined
GO{
a:undefined;
}将声明函数添加进GO对象呢,值为函数体
GO{
a:undefined;
function test() { … };
}预编译完成,执行代码:输出a,此时a的值为:undefined
a赋值,此时GO对象内的a值变为100
GO{
a:100;
function test() { … };
}执行函数test(),生成AO对象
AO{}
将函数声明的变量添加进AO对象,值为undefined
AO{
a:undefined;
}函数没有传递参数,跳过函数预编译的第三步
函数里面没有声明函数,跳过函数预编译的第四步
执行函数,打印a,此时AO里面有属性a,则输出AO里面a的值,即输出: undefined
AO的a赋值200,此时AO的a的值为200
AO{
a:200;
}输出a的值:200
将300的值赋值给b再赋值给a,此时b未声明,所以b为全局变量,将b添加进GO对象内,将a的值改为300
GO{
a:100;
function test() { … };
b:300;
}
AO{
a:300;
}输出a的值:300,函数执行完成
输出b的值:300
输出a的值,此时a的值为GO里面的值:100
2.求输出结果
//GO{
//生成的GO对象
//a:undefined;
//test:function test(){ ... };
//}
function test() {
console.log(b);//undefined
if (a) {//AO里面没有a的属性,自动寻找GO里面a的属性,此时a的值为undefined,语句不执行
var b = 50;
}
console.log(b);//undefined
c = 100;//未声明则为全局变量,添加在GO对象内
console.log(c); //100
}
var a;
test();//执行函数预编译
//AO{
//生成的AO对象
//b:undefined;
//}
a = 10;//GO里面的a赋值为10
console.log(c);//100
function test() {
return a;
a = 1;
function a() {
var a = 2;
}
}
console.log(test());
// AO: {
// a: function a() { },
// }
//ƒ a() {
// var a = 2;
// }
function test() {
a = 1;
function a() { };
var a = 2;
return a;
}
console.log(test());
// AO: {
// a: undefined ->
// a(){ } ->
// 1 ->
// 2,
// }
a = 1;
function test(e) {
function e() { }
arguments[0] = 2; //与实参同步
console.log(e); //2
if (a) {
var b = 3; // 这里也会变量提升,不要忘记
}
var c;
a = 4;
var a;
console.log(b); //undefined
f = 5;
console.log(c); // undefined
console.log(a); // 4
}
var a;
test(1);
// GO: {
// a: undefined ->
// 1,
// test: test(){ },
// }
// AO: {
// e: undefined ->
// 1 ->
// e(){ } ->
// 2,
// b: undefined,
// c: undefined,
// a: undefined ->
// 4,
// }
AO、GO实际上是为了解决作用域、作用域链相关所产生的一切问题
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)