预解析-案例

js预解析

js引擎在代码正式执行之前会做一个与处理的工作:

  1、收集变量

  2、收集函数

  依据:

    将变量通过var提前到当前作用域最前面声明但不赋值var username = undefined

    函数提前到当前作用域最前面定义

1 console.log(username);  //undefined,不会报错,因为会提前声明
2 var username = 'shelly'; 
3 console.log(username)  //shelly
4 
5 fun(); //输出fun(),不会报错,因为函数会被就提前定义
6 function fun(){
7     console.log('fun()');
8 }

上边的代码经过预解析之后就是这样

1 var username;
2 function fun(){
3     console.log('fun()');
4 }
5 console.log(username);  //undefined,不会报错,因为会提前声明
6 username = 'shelly'; 
7 console.log(username)  //shelly
8 fun(); //输出fun(),不会报错,因为函数会被就提前定义

案例一:

 1 function Foo(){
 2     getName = function(){ alert(1);};
 3     return this;
 4 }
 5 Foo.getName = function(){ alert(2);};
 6 Foo.prototype.getName = function(){ alert(3);};
 7 var getName = function(){ alert(4);};
 8 function getName(){ alert(5);};
 9 
10 // 请写出下列的输出结果
11 Foo.getName();
12 getName();
13 Foo().getName();
14 getName();
15 new Foo.getName();
16 new Foo().getName();
17 new new Foo().getName();

按代码执行顺序,先进行预解析,即变量和函数的提升。

1 function Foo(){
2     getName = function(){ alert(1);};
3     return this;
4 }
5 var getName; //该getName会被第六行的getName覆盖
6 function getName(){ alert(5);}; //该getName会被第9行的getName覆盖
7 Foo.getName = function(){ alert(2);};
8 Foo.prototype.getName = function(){ alert(3);};
9 getName = function(){ alert(4);};

解析完后的代码

1 function Foo(){
2     getName = function(){ alert(1);};
3     return this;
4 }
5 Foo.getName = function(){ alert(2);};
6 Foo.prototype.getName = function(){ alert(3);};
7 getName = function(){ alert(4);};
第一问:Foo.getName();  //2  
找到Foo这个构造函数(也是个对象),调用它的静态方法
第二问:getName();   //4   
预解析后 getName = function(){ alert(4);};

第三问:Foo().getName(); //1
调用Foo这个构造函数,调用就会执行Foo内部的代码,该局部作用域也会进行预解析,执行到getName时,发现他没有进行声明,就会去全局下找有没有这个getName,
如果没有就声明一个,如果有就会重新赋值。所以此时,getName = function(){ alert(1);};
return this, 函数内部的this具体指谁,要看调用它的是谁就是看.前面是谁。Foo()其实省略了window.Foo();因此this指向window
最后变成了window.getName(); 也就是被重新赋值后的getName 。是1

第四问: getName(); //1
经过第三问的分析,此时window.getName = function(){ alert(1);};

第五问: new Foo.getName(); //2

整理下优先级:new (Foo.getName()); 先调用Foo的静态方法,就变成 new (function(){ alert(2);};)()

了解下new过程会做些什么
1、创建空对象;var obj = {};
2、设置新对象的constructor属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的prototype对象;
  obj.__proto__ = ClassA.prototype;
3、使用新对象调用函数,函数中的this被指向新实例对象:

  ClassA.call(obj);  //{}.构造函数(); 执行构造函数内的代码,并把this指向obj  
4、返回新对象obj
总而言之,new的过程会执行一次函数内的代码,也就是弹出个2

第六问: new Foo().getName(); //3
整理下优先级 (new Foo()).getName(); new Foo()会得到一个Foo的实例对象,再调用实例对象上的getName方法,
实例对象上的方法会先搜索实例本身看有没有该方法,没有则搜索原型上有没有,没有才会搜索构造函数本身。明显,原型上有,所以会弹出3

第七问: new new Foo().getName(); //3
整理下优先级:new ((new Foo()).getName());参照第五问和第六问的原理,会弹出3


 
奔跑的蜗牛
原文地址:https://www.cnblogs.com/xiaoyue-/p/10653949.html