js-做用域

如下內容/例子均爲抄襲  (js教程:javascript做用域(Scope) 文章寫得太好了,全抄完了- -!)javascript

 

//呼叫對象方法
var deep_thought = { 
the_answer: 42, 
ask_question: function () { 
return this.the_answer;      
} 
}; 
var the_meaning = deep_thought.ask_question(); 
-----------------------------------------------------
 //剛建立的函數/構造函數
function BigComputer(answer) { 
this.the_answer = answer;       
this.ask_question = function () { 
return this.the_answer; 
} 
} 
var deep_thought = new BigComputer(42); 
var the_meaning = deep_thought.ask_question();//建立後的對象
-----------------------------------------------------
 //呼叫函數
function test_this() { 
return this;                                  
} 
var i_wonder_what_this_is = test_this(); 

以上是this比較容易理解的幾個做用java

//事件處理函數
function click_handler() { 
alert(this); // 彈出 window 對象 ,可是調用對象不一樣,返回this不一樣..
}  -------
<button id='thebutton' onclick='click_handler()'>Click me!</button>

此時,直接調用的this與添加事件中的this是2個不一樣的this,這種添加方法的方式是不利於理解與閱讀的,解決以下數組

function click_handler() { 
alert(this); // 彈出按鈕的DOM節點 
} 

function addhandler() { 
document.getElementById('thebutton').onclick = click_handler; 
} 
window.onload = addhandler; 
------- 
<button id='thebutton' >Click me!</button>

我有說過要嚴格講js與前臺代碼分離的事情嗎?這就是其中一個緣由閉包

複雜的狀況來了,咱們須要詢問deep_thought一個問題,若是不是直接運行click_handler而是經過點擊按鈕的話,那會發生什麼事情?解決此問題的代碼貌似十分直接,咱們可能會這樣作:app

<script type="text/javascript"> 
function BigComputer(answer) { 
this.the_answer = answer; 
this.ask_question = function () { 
alert(this.the_answer); //表明產生時間的DOM元素
} 
} 
function addhandler() { 
var deep_thought = new BigComputer(42), 
the_button = document.getElementById('thebutton'); 
the_button.onclick = deep_thought.ask_question; 
} 
window.onload = addhandler; 
</script> 

其實問題顯而易見:咱們給ask_question傳遞一個引用,它做爲一個事件處理函數來執行,與做爲對象方法來運行的上下文並不同。簡而言之,ask_question中的 this關鍵字指向了產生事件的DOM元素,而不是在BigComputer的對象中。DOM元素並不存在一個the_answer屬性,因此咱們獲得的是 undefined而不是」42″. setTimeout也有相似的行爲,它在延遲函數執行的同時跑到了一個全局的上下文中去了,這個問題會在程序的全部角落時不時忽然冒出,若是不細緻地追蹤程序的每個角落的話,仍是一個很是難以排錯的問題,尤爲在你的對象有跟DOM元素或者window對象同名屬性的時候。 函數

使用.apply()和.call()掌控上下文 在點擊按鈕的時候,咱們真正須要的是可以諮詢deep_thought一個問題,更進一步說,咱們真正須要的是,在應答事件和setTimeout的呼叫時,可以在自身的本原上下文中呼叫對象的方法。有兩個不爲人知的javascript方法,apply和call,在咱們執行函數呼叫時,能夠曲線救國幫咱們達到目的,容許咱們手工覆蓋this的默認值。咱們先來看call: 工具

<script type="text/javascript"> 
var first_object = { 
num: 42 
}; 
var second_object = { 
num: 24 
}; 

function multiply(mult) {              //multiply()至關於 multiply.call(this);此時this所表明的對象至關明確
return this.num * mult;                                     
} 
 
multiply.call(first_object, 5); // 返回 42 * 5                                     
multiply.call(second_object, 5); // 返回 24 * 5 
</script>

 

在這個例子中,咱們首先定義了兩個對象,first_object和second_object,它們分別有本身的num屬性。而後定義了一個multiply函數,它只接受一個參數,並返回該參數與this所指對象的num屬性的乘積。若是咱們呼叫函數自身,返回的答案極大多是undefined,由於全局window對象並無一個num屬性除非有明確的指定。咱們須要一些途徑來告訴multiply裏面的this關鍵字應該引用什麼。而multiply的call方法正是咱們所須要的。
call的第一個參數定義了在業已執行的函數內this的所指對象。其他的參數則傳入業已執行的函數內,如同函數的自身呼叫通常。因此,當執行multiply.call(first_object, 5)時,multiply被呼叫,5傳入做爲第一個參數,而this關鍵字被設置爲first_object的引用。一樣,當執行multiply.call(second_object, 5)時,5傳入做爲第一個參數,而this關鍵字被設置爲second_object的引用。
apply以call同樣的方式工做,但可讓你把參數包裹進一個數組再傳遞給呼叫函數,在程序性生成函數呼叫時尤其有用。使用apply重現上一段代碼,其實區別並不大:
this

<script type="text/javascript"> 
... 

multiply.apply(first_object, [5]); // 返回 42 * 5 
multiply.apply(second_object, [5]); // 返回 24 * 5 
</script> 

apply和call自己都很是有用,並值得貯藏於你的工具箱內,但對於事件處理函數所改變的上下文問題,也只是送佛到西天的中途而已,剩下的仍是得咱們來解決。在搭建處理函數時,咱們天然而然地認爲,只需簡單地經過使用call來改變this的含義便可:
spa

function addhandler() { 
var deep_thought = new BigComputer(42), 
the_button = document.getElementById('thebutton'); 

the_button.onclick = deep_thought.ask_question.call(deep_thought); 
} 

代碼之因此有問題的理由很簡單:call當即執行了函數(譯註:其實能夠用一個匿名函數封裝,例如the_button.onclick = function(){deep_thought.ask_question.call(deep_thought);},但比起即將討論的bind來,依然不夠優雅)。咱們給onclcik處理函數一個函數執行後的結果而非函數的引用。因此咱們須要利用另外一個javascript特點,以解決這個問題。 .bind()之美 我並非 Prototype javascript framework的忠實粉絲,但我對它的整體代碼質量印象深入。具體而言,它爲Function對象增長一個簡潔的補充,對我管理函數呼叫執行後的上下文產生了極大的正面影響:bind跟call同樣執行相同的常見任務,改變函數執行的上下文。不一樣之處在於bind返回的是函數引用能夠備用,而不是call的當即執行而產生的最終結果。
若是須要簡化一下bind函數以抓住概念的重點,咱們能夠先把它插進前面討論的乘積例子中去,看它到底是如何工做的。這是一個至關優雅的解決方案:
prototype

<script type="text/javascript"> 
var first_object = { 
num: 42 
}; 
var second_object = { 
num: 24 
}; 

function multiply(mult) { 
return this.num * mult; 
} 

Function.prototype.bind = function(obj) { 
var method = this, 
temp = function() { 
return method.apply(obj, arguments); 
}; 

return temp; 
} 

var first_multiply = multiply.bind(first_object); 
first_multiply(5); // 返回 42 * 5 

var second_multiply = multiply.bind(second_object); 
second_multiply(5); // 返回 24 * 5 
</script> 

 

首先,咱們定義了first_object, second_object和multiply函數,一如既往。細心處理這些後,咱們繼續爲Function對象的prototype定義一個bind方法,這樣的話,咱們程序裏的函數都有一個bind方法可用。當執行multiply.bind(first_object)時,javascript爲bind方法建立一個運行上下文,把this置爲multiply函數的引用,並把第一個參數obj置爲first_object的引用。目前爲止,一切皆順。
這個解決方案的真正天才之處在於method的建立,置爲this的引用所指(即multiply函數自身)。當下一行的匿名函數被建立,method經過它的做用域鏈訪問,obj亦然(不要在此使用this, 由於新建立的函數執行後,this會被新的、局部的上下文覆蓋)。這個this的別名讓apply執行multiply函數成爲可能,而傳遞obj則確保上下文的正確。用計算機科學的話說,temp是一個閉包(closure),它能夠保證,須要在first_object的上下文中執行multiply,bind呼叫的最終返回能夠用在任何的上下文中。
這纔是前面說到的事件處理函數和setTimeout情形所真正須要的。如下代碼徹底解決了這些問題,綁定deep_thought.ask_question方法到deep_thought的上下文中,所以能在任何事件觸發時都能正確運行:

function addhandler() { 
var deep_thought = new BigComputer(42), 
the_button = document.getElementById('thebutton'); 

the_button.onclick = deep_thought.ask_question.bind(deep_thought); 
} 
相關文章
相關標籤/搜索