什么是预编译
就是代码在执行之前, 先检查一下, 代码是否有语法错误, 初始化作用域, 变量提升, 函数提升, 等操作, 这个过程就称之为预编译
js代码被js引擎执行的过程
- 预编译(检查语法错误, 变量提升, 初始化作用域)
- 解释代码, 执行代码(解释一行就执行一行)
预编译现象
javascript
console.log("111");
console.log("222"]; // 这行的 ] 打错了
console.log("333");以上代码是不会执行的, js 执行引擎在预编译过程就检测到了语法错误, 所以代码是不会执行的,无论是错误执行之前还是之后,都不执行
变量提升, 函数提升
- 所谓变量提升, 就是通过
var关键字声明的变量, 或者function声明的函数(函数提升), 在代码预编译过程就被自动放到,变量所在作用域的最前面去声明,具体实例请查看下面代码: 1.变量提升现象 - 变量赋值是没有
变量提升现象的具体实例请查看下面代码: 2.函数提升现象 - 函数提升是把声明的函数整个提升, 而变量提升只是把声明放到最前面, 但是赋值还是在代码执行过程去赋值
具体实例请查看下面代码: 3.变量赋值不会提升现象
javascript
// 1. 变量提升现象
console.log(a); // undefined
var a = 10;
console.log(a); // 10
// 2. 函数提升现象, 可以在函数声明之前, 直接调用
test(); // test
function test() {
console.log('test');
}
// 3.变量赋值不会提升的现象, var test = function 是一个赋值语句
console.log(typeof test2); // undefined
var test2 = function () {
console.info('test');
};
console.log(typeof test2); // function以上代码预编译过程
- 检测语法错误
- 变量提升, 函数提升
var a = undefinedvar test2 = undefinedfunction test() {}
以上代码执行过程
console.log(a);所以此时获取到的值是:undefineda = 10此时才给变量 a 赋值console.log(a);所以此时获取到的a是:10test();因为在预解析阶段已经函数提升了, 所以可以在声明函数的代码之前调用函数console.log(typeof test2);变量赋值并不会出现函数提升的现象, 所以此时是undefinedtest2 = function {}此时才给test赋值console.log(typeof test2);因为已经赋值了, 所以此时是function
暗示全局变量 imply global variable
没有用 var 声明的变量, 而是直接赋值, 默认会放到全局对象 window 上
javascript
function test() {
var userid = 101;
username = 'tom';
}
test();
console.log(window.username); // tom
console.log(window.userid); // undefined函数上下文 activation object
虽然我们看不到, 但是可以确定的是: 函数在预编译过程, 就会初始化活跃对象也叫``函数上下文 context
函数上下文的初始化过程
- 寻找形参, 并且将实参赋值给对应形参
- 变量提升,函数提升
- 执行
javascript
function test(a) {
console.info(typeof a); // function
var a = 1;
function a() {}
console.info(typeof a); // number
var b = function () {};
console.info(typeof b); // function
}
test(5);以上代码在预编译过程
function test(a) {}a = 5寻找形参,将实参的值赋值给形参var a = undefined;变量提升var b = undefined变量提升function a(){}函数提升
以上代码在执行过程
test(5)console.info(typeof a)此时的 a 是functiona=1此时 a 是numberconsole.info(typeof a);此时 a 是numberb = function () {}给b赋值console.info(typeof b);此时的b是function
全局上下文 global object
注意: 全局上下文在浏览器环境下是: window , 但是在 node.js 环境下, 全局变量是 global
其实, 全局上下文和函数上下文初始化的过程非常相似, 可以将全局上下问看做 没有寻找形参, 并且将实参赋值给对应形参, 那一个步骤的特殊 "函数上下文"
全局上下文初始化过程
- 变量提升 + 函数提升
- 执行
javascript
console.log(typeof a); // function
var a = 1;
function a() {}
console.log(a); // 1以上代码在预编译过程
var a = undefined;变量提升function a(){}函数提升, 此时的 a 的值是function a(){}
以上代码在执行过程
console.log(typeof a);此时的 a 的值是function a(){}a = 1给变量a赋值console.log(a);此时 a 的值是1
练习1
javascript
var b = 3;
var c = 7;
console.log(typeof a); // function
function a(a) {
console.log(typeof a); //function
var a = 2;
console.log(a); // 2
function a() {}
var b = 5;
console.log(b); // 5
console.log(c); // 7
}
a(1);以上代码预编译阶段
var b = undefind;变量提升var c = undefined;变量提升function a(){}函数提升a = 1;寻找形参,将实参的值赋值给形参var a = undefind;变量提升var b = undefind;变量提升function a (){}函数提升
以上代码执行阶段
b = 3;给变量b赋值c = 7;给变量c赋值console.log(typeof a);此时的a是functiona()执行函数aconsole.log(a);此时的a是functiona = 2;给变量a赋值console.log(a);此时的a是2b = 2;给变量b赋值console.log(b);此时的b是2console.log(c);此时的c是7
练习2
javascript
a = 1; // 这只是个赋值语句, 并没有用 var声明, 所以不会变量提升
function test() {
console.info(a); // 此时这个a获取的是test函数内部的a, 第6行, undefined
a = 2;
console.info(a); // 2
var a = 3;
console.info(a); // 3
}
console.log(a); // 1
test();
console.log(a); // 1以上代码预编译阶段
function test(){}函数提升var a = undefined;变量提升
以上代码执行过程
a = 1;给变量a赋值console.log(a);此时a的值是1test();执行test函数console.info(a);此时函数内部的a的值是undefinda = 2;给变量a赋值console.info(a);此时a的值是2a = 3;给变量a赋值console.info(a);此时a的值是3
console.log(a);此时a的值是1, 因为无论函数内部的变量怎么变, 都和外面的这个 a 没有关系
练习3
javascript
function test() {
console.log(b); // undefined
if (a) {
var b = 2; // 判断是执行阶段才会执行, 所以 var b 会变量提升(预编译阶段)
}
c = 3;
console.log(c); // 3
}
var a;
test();
a = 1;
console.log(a); // 1以上代码预编译过程
var a = undefined;变量提升function test(){}函数提升var b = undefined;函数中, 变量提升
以上代码执行过程
console.log(b);此时b的值是undefinedif (a) { var b = 2 }此时a的值是undefined所以b不会被赋值c = 3暗示全局变量window.c = 3console.log(c);此时c的值是ca = 1;给变量a赋值console.log(a);此时a的值是 1
练习4
javascript
function test() {
return a;
a = 1;
function a() {}
var a = 2;
}
console.log(test()); // function a() {}以上代码预编译过程
function test(){}函数提升var a = undefined;变量提升function a(){}函数提升, 此时, test函数内a的值为function a(){}
以上代码执行过程
console.log(test());return a;此时的a是test函数内部的function a(){}
练习5
javascript
function test() {
a = 1;
function a() {}
var a = 2;
return a;
}
console.log(test()); // 2以上代码预编译过程
function test() {}函数提升var a = undefined;变量提升function a(){}函数提升, 所以此时test内部变量a的值为function a(){}
以上代码执行过程
console.log(test())a = 1;暗示全局变量a = 2;给函数内部的变量a赋值, 所以此时test内部变量a的值为2- 所以console.log打印出
2
练习6
javascript
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
}
var a;
test(1);
console.log(a); // 4;上面代码预编译过程
var a = undefined;变量提升function test(){}函数提升e = 1;寻找形参, 并且将实参赋值给对应形参var b = undefined;变量提升var c = undefined;变量提升var a = undefined变量提升function e(){}函数提升, 所以此时e的值是function() {}
上面代码指定指定阶段
a = 1;给变量a赋值test(1);调用 test 函数arguments[0] = 2;修改实参的值为 2,argument[0]的值, 代表的就是形参e的值console.log(e);此时的 e 是2if (a) {b = 3}此时a的值是undefined所以,b=3不会被执行a = 4;给test函数内部变量a赋值console.log(b);此时, b的值是undefined
练习7
Q: 以下代码会在控制台打印什么 A: 会打印 通过了
javascript
if (typeof a && -true + +undefined + '') {
console.log('通过了');
} else {
console.log('不通过');
}解题思路: 虽然看起来很复杂, 其实只要看每一个判断条件都隐式转换后的值就能知道结果了
typeof(a)->'undefined'(-true) + (+undefined) + ''->-1 + NaN + ''->'NaN''undefined' && 'NaN'-> true
练习8
Q: 以下代码会在控制台打印什么 A: 会打印 通过了
javascript
if (1 + 5 * '3' === 16) {
console.log('通过了');
} else {
console.log('不通过');
}解题思路: 和数学中的计算一样, 会先计算乘法(计算的时候会 隐式类型转换 ), 然后在计算加法
练习9
Q: 以下代码会在控制台打印什么 A: 会打印 1
javascript
var res = !!' ' + !!'' - !!false || '通过了';
console.log(res); // 1解题思路:
- 因为
||运算符的优先级是最低的, 所以只要知道||左边的值true还是false是多少就知道打印什么了 !!' '-> true:两个!!的作用抵消了!!''-> false两个!!的作用抵消了-!!false->-false(-运算符隐式转换成Number类型) ->0true + false - 0->1 + 0 - 0->11 || '通过了'->1