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