js 高程 22.1.4 函數綁定 bind() 封裝分析

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

image

爲何呢?書上原話:app

這個問題在於沒有保存 handler.handleClick() 的環境,因此 this 對象最後是指向了 DOM 按鈕而非 handler函數

我理解就是就是,上面的事件綁定代碼其實就至關於:測試

document.body.onclick = function(event) {
    console.log(this);
};

那麼這個地方,事件綁定在誰身上, this 固然就指向誰(事件處理程序中的 this 不清楚的點這裏),因此這裏輸出 body 對象也是很合理的,若是要輸出 handler 對象,單獨執行 handler.handleClick() 就能夠了優化

image

這裏也很好理解,handleClick 做爲 handler 對象的方法執行的,那麼其中的 this 固然指向的是 handler,結合這種狀況,要改上面的代碼就要這樣:this

document.body.onclick = function(event) {
    console.log(this);  //這裏的 this 固然仍是 body
    handler.handleClick(); // 可是這裏面執行的 this 就是 handler 對象了
};

image

可是這樣寫的缺點很明顯,本來就是想經過下面一行代碼實現的,如圖:spa

document.body.onclick=handler.handleClick; //最初
document.body.onclick = function(event) {  //如今
     handler.handleClick();
};

如今明顯繁瑣了些,並且最初的一行代碼理論上也應該是能夠實現的,因此如今的代碼須要優化封裝下,來達到一樣的效果,封裝的時候,要實現兩個目的code

  1. 調用函數的時候執行 handler.handleClick(); 且要保存它的執行環境(也就是要肯定執行時的 this ,指向的是 handler,而不會變成其餘的對象)
  2. 要把 event 對象傳遞過去

看到須要改變 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);
        };
    }

書上原文的結果也是同樣的:

image

bind 函數 推導出來後,就簡單了,原來的代碼就變成了

document.body.onclick=handler.handleClick; //最初錯誤代碼
document.body.onclick = function(event) {  //後來改善代碼
     handler.handleClick();
};
document.body.onclick = bind(handler.handleClick,handler); //最後封裝 bind 函數

點擊 body 後,就返回的 handler 對象,這樣就能夠訪問 handler 的屬性了;

image

 

不過,ES5 已經爲全部函數定義了一個原生的 bind() 方法,進一步簡單了操做,像上面的代碼用 ES5 實際上就是一句

document.body.onclick = handler.handleClick.bind(handler); 

因此前面都是廢話,- -

相關文章
相關標籤/搜索