JavaScript预编译

JavaScript预编译

JavaScript运行三部曲

  1. 语法分析:先将代码全部扫一遍,看有没有语法错误
  2. 预编译(执行前一刻):变量和函数声明提升
  3. 执行:逐行执行

预编译

  1. 在当前的作用域中,JS代码执行之前,浏览器首先会默认地把所有带var、function的进行提前的声明或者定义
  2. var变量声明只会声明,不会定义,赋值undefined
  3. function函数声明会声明和定义,赋值函数体

全局中的预编译

  1. 创建全局对象
  2. 找到全局里的变量声明,将变量声明为全局对象的属性名,赋值为undefined
  3. 找到全局里的函数声明,将函数名作为全局对象的属性名,赋值为函数体

函数中的预编译

  1. 创建AO对象(执行上下文)
  2. 找到形参,将形参作为AO属性名;如果有实参,赋值实参;否则,赋值undefined
  3. 找到变量声明,将变量作为AO的属性名,赋值undefined
  4. 找到函数声明,将函数名作为AO的属性名,赋值函数体

块级作用域中的预编译

  1. var变量声明没有变化
  2. function函数声明会提升到全局作用域或函数作用域的头部,类似于var
  3. 同时,function函数声明会提升到块级作用域的头部
if(false) {
  function f() {}
}
//等价于
var f;//var f= undefined;全局作用域
if(false) {
  function f() {}
}
 

预编译案例

变量和函数同名

当变量和函数同名时,只留下函数的值,不管谁前谁后,所以函数声明的优先级更高

console.log(b);
function b() {
  console.log('bbb');
}
var b = 2;
//解析过程
//预编译
function b() {console.log('bbb');}
var b;
//逐行执行
console.log(b);//[Function: b]
b = 2;
 
console.log(a);
var a = 1;
function a() {
  console.log(2);
}
console.log(a);
var a = 3;
console.log(a);
function a() {
  console.log(4);
}
//解析过程
//预编译
var a;
function a() {console.log(2);}
function a() {console.log(4);}
//逐行执行
console.log(a);//function a() {console.log(4);}
a = 1;
console.log(a);//1
a = 3;
console.log(a);//3
 

函数中的预编译

函数中的预编译要等到函数执行前的一刻才进行预编译

var a = 10;
function f1() {
  var b = 2 * a;
  var a = 20;
  var c = a + 1;
  console.log(b);
  console.log(c);
};
f1();
//解析过程
//预编译
var a;
function f1() {...}
//逐行执行
a = 10;
f1();
  //函数中的预编译
  var b;
  var a;
  var c;
  //函数中的逐行执行
  b = 2 * a;//2 * undefined
  a = 20;
  c = a + 1;
  console.log(b);//NaN
  console.log(c);//21
 

函数表达式的预编译

console.log(fn);
var fn = function() {
  console.log('ok');
};
console.log(fn);
//解析过程
//预编译
var fn;
//逐行执行
console.log(fn);//undefined
fn = function() {console.log('ok');};
console.log(fn);//[Function: fn]
 

函数传参的预编译

函数中的形参是函数中声明的变量

function fun(n) {
  console.log(n);
  var n = 456;
  console.log(n);
}
var n = 123;
fun(n);
//解析过程
//预编译
function fun(n) {}
var n;
//逐行执行
n = 123;
fun(n);
  //函数中的预编译
  var n = 123;//实参给形参赋值,函数中已经声明变量n了,后面就不需要重新声明了
  //函数中的逐行执行
  console.log(n);//123
  n = 456;
  console.log(n);//456
 

立即执行函数的预编译

立即执行函数不会进行预编译,其它和普通函数一样,当代码逐行执行到这个位置的时候,定义和执行一起完成

(function(num) {
  console.log(num);
  console.log(n);
})(100);
var n = 10;
//解析过程
//预编译
var n;
//逐行执行
(function(num) {...})(100);//函数执行
  //立即执行函数中的预编译
  var num = 100;//实参赋值给形参
  //立即执行函数中的逐行执行
  console.log(num);//100
  console.log(n);//undefined
  n = 10;
 

函数内部return的预编译

函数中return后面的代码虽然不会执行,但是会进行预编译

function fn() {
  console.log(num);
  return num;
  var num = 100;
}
fn();
//解析过程
//预编译
function fn() {...}
//逐行执行
f();
  //函数中的预编译
  var num;//return后面的代码不会执行,但会预编译
  //函数中的逐行执行
  console.log(num);//undefined
  return num;
 

if代码块中的预编译

  • 不管if判断是否成立,if中都会进行预编译;如果判断为真,就会执行if中的代码块;否则,if中只会预编译,不会执行代码。
  • 根据ES6标准入门(只对ES6的浏览器实现有效):
  1. 允许在块级作用域内声明函数(应该避免函数声明,可以写成函数表达式的形式)
  2. 函数声明类似于var,即会提升到全局作用域或函数作用域的头部
  3. 同时,函数声明还会提升到所在的块级作用域的头部
//ES6环境的浏览器
(function() {
  if(false) {
    function f() {
      console.log('ok');
    }
  }
  f();
}());
//会报错,实际运行的是以下代码
(function() {
  var f;//var f = undefined
  if(false) {
    //块级作用域中函数的声明会提升到函数作用域的头部
    function f() {
      console.log('ok');
    }
  }
  f();
}());
 
//if判断为假
console.log(num);
console.log(f);
if(false) {
  var num = 100;
  function f() {
    console.log(123);
  }
}
console.log(f);
console.log(num);
//解析过程
//预编译
var num;
//块级作用域中函数的声明会提升到全局作用域的头部
var f;//块级作用域中函数的预编译类似于var,先声明后赋值
//逐行执行
console.log(num);//undefined
console.log(f);//undefined
//由于if判断为假所以if代码块中的代码不执行
console.log(f);//undefined
console.log(num);//undefined
 
//if判断为真
console.log(num);
console.log(f);
if(true) {
  var num = 100;
  function f() {
    console.log(123);
  }
}
console.log(f);
console.log(num);
//解析过程
//预编译
var num;
//块级作用域中函数的声明会提升到全局作用域的头部
var f;//块级作用域中函数的预编译类似于var,先声明后赋值
//逐行执行
console.log(num);//undefined
console.log(f);//undefined
//if判断条件为真,执行if代码块中的代码
num = 100;
f = function() {console.log(123);}
console.log(f);//[Function: f]
console.log(num);//100
 

if代码块中函数声明前后的作用域不同

console.log(a);
var a = 0;
if(true) {
  console.log(a);
  a = 1;
  console.log(a);//1
  function a() {}
  a = 21;
  console.log(a);
}
console.log(a);
//解析过程
//预编译
var a;
//逐行执行
console.log(a);//undefined
a = 0;
  //if判断为真
  function a() {}//预编译,函数声明会提升到块级作用域的头部
  console.log(a);//[Function: a]
  a = 1;//改变全局变量的值
  console.log(a);//1
  //函数名和全局变量名同名
  function a() {}//函数后面是另一个作用域,函数上面是全局作用域,后边是局部作用域
  a = 21;//局部作用域中的值
  console.log(a);//21
console.log(a);//1
 

原创文章,作者:我心飞翔,如若转载,请注明出处:https://www.pipipi.net/14780.html

发表评论

登录后才能评论