预编译
文章目录
js运行三部曲
//js:单线程解释语言(翻译一句执行一句)
语法分析
// 在语句执行之前编译器会通篇扫描一遍看有没有低级错误
// 这个过程就是语法分析的过程
预编译
预编译前奏
test(); // 在这里也能执行函数
function test() {
conloe.log('a');
}
test(); // 在这里能执行函数
var a = 123;
conloe.log('a'); // 打印123
conloe.log('a'); // 打印undefined
var a = 123;
conloe.log('a'); // 直接打印会报错,因为变量没声明
函数声明整体提升(不管函数写在哪里,系统都会把函数提到最前面,所以test()放函数上面能执行)
变量声明提升
var a = 123; 等价于 var a; a = 123;
// 系统会把变量提到最前面
console.log('a');
var a = 123;
等价于:
var a;
console.log('a');
a = 123; // 所以上面例子打印的是undefined
imply global暗示全局变量:
即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。
// 全局对象是window (window就像一个仓库一样) window是一个对象
a = 123; // 未声明(没有var) 归window所有
相当于:
window {
a : 123
}
console.log(a) // 打印123 因为访问的就是window.a
// 注意这个赋值顺序 123赋值给未声明的b 然后声明a 然后把b的值赋给a
// 所以打印window.a是undefined 打印window.b是123
// 因为a不属于window(a是定义在函数中的局部变量)
var a = b = 123; // 123赋值给b b再赋值给a 连续赋值
function test() {
var a = b = 123;
}
test();
一切声明的全局变量,全是window的属性。
var a = 123;===> window.a = 123;
function test() {
var b = 123;
}
test();
console.log(window.b); // 打印结果是undefined 因为b是函数中定义的局部变量
// 3.window就是全局
var a = 123;
console.log(a); --> console.log(window.a);
var a = 123;
var b = 234;
var c = 345;
==>window {
a : 123,
b : 234,
c : 345
}
预编译四部曲:
创建AO对象
创建AO对象就是Activation Object(活跃对象)
活跃对象就是执行期上下文,也可以理解为作用域。也就是函数产生的一个存储空间库
// 预编译发生在函数执行前的那一刻
function fn(a) { // a是形参
console.log(a);
var a = 123; // 123赋值给a
console.log(a);
function a () {}
console.log(a);
var b = function () {} // function () {}赋值给b
console.log(b);
function d () {}
}
fn(1);
// 1.创建AO对象
AO {
}
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
function fn(a) { // a是形参
console.log(a);
var a = 123; // 123赋值给a
console.log(a);
function a () {}
console.log(a);
var b = function () {} // function () {}赋值给b
console.log(b);
function d () {}
}
fn(1);
// 2.找形参和声明的变量,放到AO对象中,值是undefined,
// 因为a既是形参又是变量,所以只放一次
AO {
a : undefined,
b :undefined,
}
将实参值和形参统一
function fn(a) { // a是形参
console.log(a);
var a = 123; // 123赋值给a
console.log(a);
function a () {}
console.log(a);
var b = function () {} // b是声明的变量
console.log(b);
function d () {}
}
fn(1); // 实参是1
// 3.将实参值和形参相统一 实参的值1赋值给a
AO {
a : 1,
b :undefined,
}
在函数体里面找函数声明,值赋予函数体
function fn(a) { // a是形参
console.log(a);
var a = 123; // 123赋值给a
console.log(a);
function a () {}
console.log(a);
var b = function () {} // function () {}赋值给b
console.log(b);
function d () {}
}
fn(1); // 实参是1
// 4.在函数体里面找到函数声明,值赋予函数体,也就是把函数体赋值给函数
// 函数体:function a () {} 和 function d () {} 因为a有了不用放 只赋值 把d放进去
AO {
a : function a () {},
b :undefined,
d : function d () {}
}
预编译完成之后开始执行函数
预编译练习
预编译创建AO对象的目的就是先把东西放好,方便执行的时候去用。
// 预编译练习①
function fn(a) { // a是形参
console.log(a); // 这里打印的是AO对象中的数据 也就是function a () {}
var a = 123; // // 123赋值给a
console.log(a); // 这里打印的是AO对象中的数据 也就是123
function a () {}
console.log(a); // 这里打印的是AO对象中的数据 也就是123
var b = function () {} // function () {}赋值给b
console.log(b); // 这里打印的是AO对象中的数据 也就是function () {}
function d () {}
}
fn(1); // 实参是1
// 4.在函数体里面找到函数声明,值赋予函数体,也就是把函数体赋值给函数
// 函数体:function a () {} 和 function d () {} 因为a有了不用放 只赋值 把d放进去
AO {
a : function a () {},
b :undefined,
d : function d () {}
}
// 预编译练习②
function test(a, b) {
console.log(a); // 打印AO的初始值1
c = 0; // 0赋值给c
var c;
a = 3; // 3赋值给a
b = 2; // 2赋值给b
console.log(b); // 打印2,因为b被赋值
function b () {}
function d () {}
console.log(b); // 打印2,因为b被赋值
}
test(1); //这里只写一个参数默认是给第一个形参
// 1.创建AO对象 AO {}
// 2.找形参和变量并赋值undefined
AO {
a : undefined,
b : undefined,
c : undefined
}
// 3.实参和形参值统一
AO {
a : 1,
b : undefined,
c : undefined
}
// 4.找到函数体并赋值
AO {
a : 1,
b : function b () {},
c : undefined,
d : function d () {}
}
// 预编译练习③
function test(a,b) {
console.log(a); // 打印AO中的初始值function a () {}
console.log(b); // 打印AO中的初始值undefined
var b = 234; // 234赋值给b
console.log(b); // 打印234
a = 123; // 123赋值给形参a
console.log(a); // 打印123
function a () {} // 这里没有赋值过程,所以a的值不变
var a; // 这里没有赋值过程,所以a的值不变
b = 234; // 234赋值给b
var b = function () {} // function () {}赋值给形参b
console.log(a); // 打印123
console.log(b); // 打印function () {}
}
test(1); //这里只写一个参数默认是给第一个形参
// 1,创建AO对象 AO {}
// 2.找形参和变量并赋值undefined
AO {
a : undefined,
b : undefined
}
// 3.实参形参相统一
AO {
a : 1,
b : undefined
}
// 4.找到函数体并赋给AO对象
AO {
a : function a () {},
b : undefined
}
// 预编译练习④
console.log(a); // 打印的是function a() {}
// 根据预编译步骤可知 函数体是最后一步,权限最高
var a = 123;
function a() {}
全局预编译
生成了一个GO对象 Global Object
console.log(a); // 打印结果为GO初始值function a (){}
var a = 123; // 把123赋值给了a
function a (){}
console.log(a); // 打印结果为123
// 1.创建GO对象
// 2.找出形参和变量并赋值undefined
GO {
a : undefined
}
// 3.没有实参,所以跳过第三步
// 4.找到函数体并赋给AO对象
GO {
a : function a (){}
}
window === GO
也就是一个变量未经声明就赋值了,是需要放到GO中预编译的。
window === GO
console.log(window.a) ==> console.log(GO.a) ==> console.log(a)
function test() {
var a = b = 123; // b是未声明就赋值的变量,归window所有,也就是归GO所有
console.log(window.b); //打印123
console.log(a); //打印123
console.log(window.a); //打印undefined 因为window里面没有a
}
test();
// 执行test的时候会生成一个AO,变量只有a
AO {
a : undefined,
}
GO {
b : 123
}
GO/AO的优先级
// 函数预编译发生在函数执行前的一刻
// 练习一:
// 打印的时候相同名字的AO上有就打印AO上的,没有就打印GO上的
console.log(test); // 打印全局的test 也就是function test () {}
function test(test) {
console.log(test); // 打印AO中的初始值function test () {}
var test = 234; //234赋值给AO中的test
console.log(test); // 打印234
function test () {}
}
test(1);
var test = 123; // 注意这是全局的 123赋值给全局的test
GO {
test : function test () {},
}
AO {
test : function test () {},
}
// 练习二:
GO {
global : undefined, // 执行之后是100
fn : function (){...}
}
var global = 100;
function fn() {
console.log(global); // 打印100
}
// 执行前函数预编译 函数里面只有一个打印global 没有值 所以找GO GO里面有100
fn();
// 练习三:
GO {
global : 100, // 有声明全局变量,然后把值写进去
fn : function (){...}
}
global = 100;
function fn() {
console.log(global); // undefined
global = 200;
console.log(global); // 200
var global = 300;
}
AO {
global : undefined, // 函数体内有变量声明 有自己的就用自己的 没有才用GO内的
}
fn();
var global;
// 练习四
GO {
a : 100,
test : function (){...},
c : 234
}
function test () {
console.log(b); // undefined
if(a) { // if要执行的时候才有用 预编译的时候不执行 所以不看
var b = 100;
}
console.log(b); // undefined if要执行的时候才有用 预编译的时候不执行 所以没用
c = 234; // c没有声明 所以归GO所有
console.log(c); // 234
}
var a;
AO {
a : undefined,
b : undefined, // 不看if
}
test();
a = 10;
console.log(c); // 234
// 百度面试题
// 1.百度面试题一
AO {
foo : function foo() {} // 函数体权限最高
}
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = 11;
}
console.log(bar()); // function foo (){}
// 2.百度面试题二
AO {
foo : 11 // 函数体之后 11又赋值给foo 所以打印11
}
function bar() {
foo = 10;
function foo() {}
var foo = 11;
return foo;
}
console.log(bar()); // 11
// 知识回顾
// 1.创建GO/AO对象 2.找形参和变量赋值undefined
// 3.形参实参相统一 4.函数体赋值给GO/AO
console.log(b); // undefined 因为打印之前还没赋值
var b = function () {}
console.log(b); // // function () {} 因为打印之前还没赋值
console.log(b); // function b() {} 因为函数体权限最高
function b() {}
作业
// 题目一:注意AO和GO各提各的(有重复正常)
GO {
a : 100,
demo : function () {}, // 函数体
f : 123 // 函数内部赋值
}
a = 100;
function demo(e) {
function e() {}
arguments[0] = 2; // 这里是给形参e赋值为2
document.write(e); // 打印出2
if(a) { // 循环是在执行之后才起作业 所以不看
var b = 123;
function c() {
// if里面不能声明function(以前可以)
}
}
var c;
a = 10; // 给a赋值10
var a;
document.write(b); /// 打印AO中的b undefined
f = 123; // 变量未声明 归GO所有
document.write(c); // undefined
document.write(a); // 打印AO中的a 10
}
var a;
demo(1);
document.write(a); // 打印全局的a 100
document.write(f); // 123
AO {
e : 2,
b : undefined,
c : undefined,
a : 10
}
// 题目二:
var str = false + 1; // 转换成数字相加 0 + 1
document.write(str); // 1
var demo = false == 1; // false是0 所以false == 1判断结果是false false赋值给demo
document.write(demo); // 打印false
if(typeof(a)&&-true + (+undefined) + ""){
// a没定义不报错,会打印str类型的undefined str类型的undefined是true +undefined转换为str类型的NaN “”是false 转换成数字相加就是 1 + (-1) + 1 + 0 = 1
document.write('基础扎实');
}
if(11 + "11" * 2 ==33) { //*会把字符串转换成数字再进行运算 所以结果是33
document.write('基础扎实');
}
!!" " + !!"" - !!false || document.write('你觉得能打印吗?'); // true false false 1 + 0 - 0 = 1 true 一真即真 所以不往后看 所以不会打印
// " "中间有空格是空格字符串 “”中间没空格才是空串
// 百度面试题 写出foo的值
(window.foo || (window.foo = 'bar')); // || 的优先级比 = 高 所以后一半加了小括号
// 先看括号的部分 “bar”赋值给foo 然后再看window.foo