預解析-案例

js預解析shell

js引擎在代碼正式執行以前會作一個與處理的工做:函數

  一、收集變量this

  二、收集函數spa

  依據:prototype

    將變量經過var提早到當前做用域最前面聲明但不賦值var username = undefinedcode

    函數提早到當前做用域最前面定義對象

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 }

上邊的代碼通過預解析以後就是這樣blog

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();

按代碼執行順序,先進行預解析,即變量和函數的提高。get

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過程會作些什麼
一、建立空對象;var obj = {};
二、設置新對象的constructor屬性爲構造函數的名稱,設置新對象的__proto__屬性指向構造函數的prototype對象;
  obj.__proto__ = ClassA.prototype;
三、使用新對象調用函數,函數中的this被指向新實例對象:

  ClassA.call(obj);  //{}.構造函數(); 執行構造函數內的代碼,並把this指向obj  
四、返回新對象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
相關文章
相關標籤/搜索