js 高程 書中原話(斜體表示):html
22.1.4 函數綁定
另外一個日益流行的高級技巧叫作函數綁定。函數綁定要建立一個函數,能夠在特定的this 環境中
以指定參數調用另外一個函數。該技巧經常和回調函數與事件處理程序一塊兒使用,以便在將函數做爲變量
傳遞的同時保留代碼執行環境。請看如下例子:git
var handler = { message: "Event handled", handleClick: function(event) { console.log(this); //改編,方便測試,原文是下面一句 //alert(this.message); } }; document.body.onclick=handler.handleClick; //改編,方便測試,原文是後面兩句 //var btn = document.getElementById("my-btn"); //EventUtil.addHandler(btn, "click", handler.handleClick);
看起來,當咱們點擊 body 的時候,應該輸出 handler 這個對象,可是實際上輸出的是 body 元素github
爲何呢?書上原話:app
這個問題在於沒有保存 handler.handleClick() 的環境,因此 this 對象最後是指向了 DOM 按鈕而非 handler函數
我理解就是就是,上面的事件綁定代碼其實就至關於:測試
document.body.onclick = function(event) { console.log(this); };
那麼這個地方,事件綁定在誰身上, this 固然就指向誰(事件處理程序中的 this 不清楚的點這裏),因此這裏輸出 body 對象也是很合理的,若是要輸出 handler 對象,單獨執行 handler.handleClick() 就能夠了優化
這裏也很好理解,handleClick 做爲 handler 對象的方法執行的,那麼其中的 this 固然指向的是 handler,結合這種狀況,要改上面的代碼就要這樣:this
document.body.onclick = function(event) { console.log(this); //這裏的 this 固然仍是 body handler.handleClick(); // 可是這裏面執行的 this 就是 handler 對象了 };
可是這樣寫的缺點很明顯,本來就是想經過下面一行代碼實現的,如圖:spa
document.body.onclick=handler.handleClick; //最初 document.body.onclick = function(event) { //如今 handler.handleClick(); };
如今明顯繁瑣了些,並且最初的一行代碼理論上也應該是能夠實現的,因此如今的代碼須要優化封裝下,來達到一樣的效果,封裝的時候,要實現兩個目的code
看到須要改變 this 和保存執行環境的時候,應該想到 js 的函數都有內置的 apply(),call()方法,且 arguments 對象都保存了函數的參數(這個不理解能夠看 這裏),因此這裏封裝函數也不是很難了,假設封裝的函數名叫 bind ,整個封裝邏輯就是下面這樣:
//執行 bind 函數須要返回的匿名函數 function(event) { handler.handleClick(); }; //抽象化 function(event) { fn(); } //推算 bind 函數的原型(此原型非js中的原型) function bind(fn) { return function(event) { fn(); }; } //爲了保證 this 的正確傳遞,須要用 apply 方法,這裏用法不理解,請看 這裏 function bind(fn,context) { return function(event) { fn.apply(context,參數); }; } //爲了傳遞 event 參數,這裏的參數不太好理解,須要好好想一想 function bind(fn,context) { return function() { fn.apply(context,arguments); }; }
書上原文的結果也是同樣的:
bind 函數 推導出來後,就簡單了,原來的代碼就變成了
document.body.onclick=handler.handleClick; //最初錯誤代碼 document.body.onclick = function(event) { //後來改善代碼 handler.handleClick(); }; document.body.onclick = bind(handler.handleClick,handler); //最後封裝 bind 函數
點擊 body 後,就返回的 handler 對象,這樣就能夠訪問 handler 的屬性了;
不過,ES5 已經爲全部函數定義了一個原生的 bind() 方法,進一步簡單了操做,像上面的代碼用 ES5 實際上就是一句
document.body.onclick = handler.handleClick.bind(handler);
因此前面都是廢話,- -