JS预解析和常见题目总结

JS预解析是什么?

浏览器在解析JS代码时候,首先会进行一个预解析的操作。即寻找作用域中var function 关键字及其声明的变量和函数,将其进行事先声明存储。此时变量的值为undefined 函数也未调用。然后再从上到下逐行解析代码。见到操作,就修改前面存储的值,见到读取,就取得前面存储的值。遇到函数执行,就又开启一个新的域,执行预解析和逐行解读。

常见JS预解析情况类型

情况一:

console.log(a); // undefined
var a = 10;
console.log(a); // 10

原因:在JS预解析时候,首先把var=a;提前解析,最先声明。然后逐行解析JS代码。所以首先log输出的变量a为undefined,然后执行a=10;进行赋值。所以后面的log输出为10;

 

情况二:

 console.log(fun1);//函数fun1
 console.log(fun2);//undefined

 function fun1() {
     console.log('函数fun1');
 }

 var fun2 = function () {
     console.log('函数fun2');
 }

 console.log(fun1);//函数fun1
 console.log(fun2);//函数fun2

原因:同理,在JS预解析时候,首先把var=fun2;和函数function fun1(){}提前解析,最先声明。然后逐行解析JS代码。所以首先log输出的变量fun1时候为整个fun1函数, 而fun2输出打印时候,此时仅仅var fun2=undefined;还没有赋值,所以输出为undefined;然后再继续解析,var fun2=function(){};此时才给变量fun2赋值了函数,即是函数申明中的表达式申明方式。然后打印fun1和fun2.打印函数fun1和fun2。

此时可以从控制台查看具体结构和分析一致。

情况三:

var a = 10;
console.log(a); // 10
function fun2() {
    alert(a); // undefined
    var a = 20;
    alert(a); // 20
}
fun2();

分析:同理,首先先解析var a; function fun2(){...} ;然后逐行解析,a=10; log输出10,调用fun2()函数,然后在执行函数时,重新开辟一个域空间,在fun2()整个中,var a;首先被解析,然后执行alert(a); 此时弹出undefined,然后给a赋值为20;在弹出a时,值为20;

情况四:

 var a = 1;
 function fn1() {
     alert(a);//undefined
     var a = 2;
 }
 fn1();
 alert(a);//1

分析:同理可知。注意一下,函数内部是局部变量,作用域仅仅为函数的代码块即{}所包含的区域,而这个局部对象会在函数执行完毕后立即被销毁。所以fn1函数内自己定义的局部变量a的值不会影响外面a的值得变化。 所以最后a输出的值是1。

情况五:

var a = 1;
function fn1() {
    alert(a);//1
    a = 2;
}
fn1();
alert(a);//2

分析:同理可知。a和函数fn1(){} 首先被解析。然后逐行解析: a=1;给a赋值  fn1();函数调用。

注意:函数外的a是一个全局变量,在任何地方都可以访问和对值进行修改。在调用函数fn1时候,首先进行函数内部的预解析,此函数没有声明的变量。在alert(a);时候, 首先寻找自己函数内部有没有定义变量a,如果用,则使用自己申明的变量a,如果没有,则向上一级逐级寻找有无声明此变量。如果有,则使用它,没有则报错,此变量未定义。在这里,fn1函数会找到外部定义的a=1的值,然后弹出1;修改a的值,将其改为2;然后结束函数。最后弹出alert(a)时,此时a的值为2。

情况六:

var a = 1;
function fn1(a) {
    alert(a);//undefined
    a = 2;
}
fn1();
alert(a);//1

注意点:函数的形参,相当于var a; 所以在调用函数时候,函数在预解析时候,会先将形参a 声明;函数的形参作用范围也是函数内部。所以结果如上图;

情况七:

var a = 1;
function fn1(a) {
   alert(a);//1
   a = 2;
}
fn1(a);
alert(a);//1

注意点:当调用函数有实参传递给函数时候,首先先将函数的实参赋值给形参再进行函数内部的预解析。所以在调用函数时候,a=1传递了过去。会弹出1 然后将形参a修改为2;结束fn1函数;

情况八:

alert(a);//a();
var a = 1;
alert(a);//1
function a() { alert(2); }
alert(a);//1
var a = 3;
alert(a);//3
function a() { alert(4); }
alert(a);//3
alert(typeof a)//number
a();//报错 a is not a function

 分析原因:如果var同名,后面的覆盖前面的,如果函数同名,后面覆盖前面的,函数和var同名,则函数覆盖var。所以再次个案例中我们可以将代码等价于如下所示:

 var a = function() { alert(4); }
 alert(a);        //a();
 a = 1;
 alert(a);        //1
 alert(a);        //1
 a = 3;
 alert(a);        //3
 alert(a);        //3
 alert(typeof a)//number
 a();        //报错 a is not a function

相当于变量a最先预解析之后赋值为成了一个函数,然后重名的函数后者会覆盖前者定义的函数,var申明的a变量也不会再预解析了,因为同名函数优先级最高,所以我们可以显示等价于上面代码。然后逐行在逐行解析代码。首先弹出a是一个函数,然后执行a=1;的赋值操作,将a的值修改为1;然后修改为3,此时a的类型为number数值型。最后调用函数a时候,a已经被数值3的值覆盖了,不在是一个函数类型。所以最后一行会报错。a不是一个函数,不能调用。

 

情况九:

var a = 2;
function test() {
    var a = 3;
    var b = 4;
    c = 5;
    alert(a); // 3
}

test();
alert(c); // 5
alert(b); // 报错  b is not defined

注意:在JS中,没有var关键字申明的变量赋值了之后,相当于是一个全局变量。所以test()函数内部定义的c=5;相当于是一个全局变量,在任何地方都可以被访问到。而函数内部定义的b变量,在函数执行完毕之后就被销毁了,在外部弹出变量b时候,不能找到。所以会报错。

情况十:

function fn(a) {
   console.log(a); 
   var a = 2;
   function a() { }
     console.log(a)//2
}

fn(1);

分析:调用函数fn时候,首先现将实参1赋值给函数的形参a;然后再执行函数fn内部的预解析,var a 和 function函数a同名,函数优先级更高,而形参同var等价,所以最后a为一个函数类型,输入打印a时,是一个函数,然后解析a=2;修改a的值为数值型2, 调用a函数,然后在函数a中打印的a为2。

情况十一:

console.log(a);    //是函数a
function a(a) {    
   console.log(a);  //函数a
   var a = 10;      
   console.log(a);    //10
   function a() { };
      console.log(a);  //10
   }
a(1);
a = 0;
var a;    
console.log(a);    //0

同理可得;

 

 

                                

情况十二:

var a = 1;
var x = function () {
   console.log(a);     //1
};
function f() {
   var a = 2;
   x();
}
f();

注意点:此处调用函数f()时,内部嵌套了一个函数,但是并不存在父子关系,所以函数x中的a不会去函数f()中获取,而是去找a=1;所以打印1

 

情况十三:

var x = 1,
y = z = 0;

function add(n) {
   n = n + 1;
};
y = add(x);

function add(n) {
  n = n + 3;
};
z = add(x);     

console.log(x, y, z);   //1 undefined undefined

原因:因为函数没有返回最后的值,即没有return语句,所以y和n都获取不到值,所以都为undefined;

总结:

  1. 找到var,就给它一个undefined,提到最前面;
  2. 找到function声明的函数,就把函数整体提到最前面
  3. 如果var同名,后面的覆盖前面的,如果函数同名,后面覆盖前面的,函数和var同名,则函数覆盖var。
  4. 函数调用时候,重新开启一个新的域函数内部的预解析,如果调用函数时候有实参,先进行实参赋值,在进行预解析;函数的形参预解析作用同var;其他同上;
  5. JS执行预解析之后,就会逐行解析代码; 
  6. 注意全局变量和局部变量的作用范围;
  7. 函数没有返回值时候,接收的结果为undefined;

 

谢谢观看!!!

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我再来更详细地介绍一下JavaScript中的解析。 在JavaScript中,解析是指在代码执行之前,JavaScript引擎会先对代码进行一些处理,包括变量和函数声明的提升。具体来说,解析会将变量和函数的声明提升至代码的顶部,这样在代码执行时就可以直接使用这些变量和函数,而不需要考虑它们的位置。 解析的步骤如下: 1. 将所有的变量声明和函数声明提升到作用域的顶部。 2. 对于重复声明的变量和函数,只保留最后一个声明。 3. 如果变量和函数同名,函数声明会覆盖变量声明。 需要注意的是,解析只会提升变量和函数的声明,而不会提升变量的赋值和函数的定义。例如: ``` console.log(a); // 输出undefined var a = 1; console.log(a); // 输出1 foo(); // 输出hello function foo() { console.log('hello'); } console.log(b); // 报错:ReferenceError: b is not defined ``` 在上面的代码中,变量a和函数foo的声明被提升了,但是变量a的赋值和函数foo的定义不会被提升。而变量b由于没有被声明,所以在代码执行时会报错。 另外,需要注意的是,let和const关键字声明的变量不会进行解析。这是因为let和const关键字声明的变量是块级作用域,只有在声明的位置后面的代码才能访问它们。例如: ``` console.log(a); // 报错:ReferenceError: a is not defined let a = 1; console.log(a); // 输出1 ``` 在上面的代码中,由于变量a使用了let关键字声明,所以在声明之前访问它会报错。 总之,解析JavaScript中的一个重要概念,它能够帮助我们更好地理解JavaScript代码的执行顺序。但是需要注意的是,过度依赖解析会降低代码的可读性和维护性,所以在编写代码时需要尽量避免过多依赖解析

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值