Javascript經過bind()掌控this

    最近博客的更新明顯跟不上腳步,不是由於最近什麼都沒看不知道寫什麼,而是由於最近工做比較忙看了好多東西以致於一時無從下手。東西要整理的太多了,還有不少不足啊,給本身打打氣,一點點來~不能懈怠!javascript

    今天看到公司大神的一段代碼:html

 function ReplaceProcessor() {
    this._dom = {
      btnReplace : $('#ro_btnReplace'),
      btnComplete: $('#ro_btnComplete')
    };
    // Bind events
    this._dom.btnReplace.on('click', this._onReplace.bind(this));
    this._dom.btnComplete.on('click', this._onComplete.bind(this));
  }
  
  ReplaceProcessor.prototype._onReplace = function(){
   // code
   this._dom.btnComplete.html("OK");
  }

    這裏面最後兩行代碼是向DOM節點上綁定事件,"this._onReplace.bind(this)"明顯就是綁定的執行函數,在不知道具體做用的狀況下猜想一下bind()的做用可能和call或者apply相似,用來改變function執行時的上下文環境,不知道理解的對不對因此找資料來印證一下。java

    先上官網的解釋,無論咱們我的的解釋是多麼的接地氣,官方API到底仍是比較靠譜的:
ajax

bind方法會建立一個新函數,稱爲綁定函數.當調用這個綁定函數時,綁定函數會以建立它時傳入bind方法的第一個參數做爲this,傳入bind方法的第二個以及之後的參數加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數.json

    這個解釋多讀幾回仍是很靠譜的,不是很難懂。從功能描述上看和call以及apply仍是有區別的,應用的場景不太同樣,bind主要是爲了改變函數內部的this指向,這個是在ECMA5之後加入的,因此IE8一下的瀏覽器不支持,固然有兼容的辦法,不過坦白說首先對於IE8如下實在無愛,其次那種狀況下估計你也沒什麼心情用bind了吧。。。數組

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function      
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis ? this : oThis || window,
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };}

    東西就是這麼個東西,最主要的仍是應用的場景,什麼狀況下使用。本文一開始代碼中使用.bind(this)的效果就至關於將事件綁定的callback抽出來寫,可是同時還維持了函數中的this指向。原本事件綁定的處理函數通常是一個匿名函數,這裏至關於單獨抽出來從而使結構更清晰的同時,this指向的是ReplaceProcessor的實例。
瀏覽器

    這裏列舉三部分的代碼來講明bind能爲咱們作些什麼,同時它的好處在哪裏。
app

    (一)事件處理函數
dom

    所謂的事件處理函數其實就是綁定事件後的那個callback,這裏若是用上bind你的代碼應該會簡潔優雅一些,我在開篇列出的那段代碼裏就是這樣作的。
函數

var logger = {
    x: 0,       
    updateCount: function(){
        this.x++;
        console.log(this.x);
    }
}

// 下面兩段代碼的實現是同樣的

document.querySelector('button').addEventListener('click', function(){
    logger.updateCount();
});

document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));

    如何,這就是我以前說的,原本一般狀況下處理函數都要用一層匿名函數包裹一下,才能維持處理函數自己的this.這裏直接經過.bind(logger)人爲的將其執行時的this指向logger對象。

.bind()建立了一個函數,當這個函數在被調用的時候,它的 this 關鍵詞會被設置成被傳入的值(這裏指調用bind()時傳入的參數)。

    (二)setTimeout

function LateBloomer() {
  this.petalCount = Math.ceil( Math.random() * 12 ) + 1;
}

// declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout( this.declare.bind( this ), 1000 );
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' + this.petalCount + ' petals!');
};

    看一下這裏this.dclare.bind(this),至關於將LateBloomer的實例對象傳遞到declare中,是否是setTimeout簡潔了不少,同時不會破壞其餘執行函數的結構。

    (三)請完整閱讀下面的代碼

  //設立一個簡單地對象做爲「上下文」
var context = { foo: "bar" };

//一個在this上下文中指向foo變量的函數
function returnFoo () {
  return this.foo;
}

// 變量在做用域中不存在,所以顯示undefined
returnFoo(); // => undefined

// 若是咱們把它綁定在context上下文中
var bound = returnFoo.bind(context);

// 如今的做用域中有這個變量了
bound(); // => "bar"

//
// 這就是Function.prototype.bind的做用.    
//因爲returnFoo也是函數,所以它繼承了function的原型
//
// 若是你以爲享受,接着往下讀,下面更精彩
//

// 有許多方法將函數綁定在一個上下文中
// Call和Apply讓你能在上下文中調用函數
returnFoo.call(context); // => bar
returnFoo.apply(context); // => bar

// 將函數添加到對象中
context.returnFoo = returnFoo;
context.returnFoo(); // => bar

//
// 如今咱們來玩一點詭異的東西
//

// Array.prototype 中有一個叫作slice的方法
// 對一個數組調用slice,能夠返回一個從start index到end index的數組
[1,2,3].slice(0,1); // => [1]

// 所以咱們把Array.slice賦值給一個本地變量slice
var slice = Array.prototype.slice;

//如今的slice是"自由的",因爲Array.prototype中的slice通常指定了上下文
//或者默認爲this,此時slice將不起做用
slice(0, 1); // => TypeError: can't convert undefined to object
slice([1,2,3], 0, 1); // => TypeError: ...

// 可是若是咱們使用call或者apply,slice又將在一個上下文中執行
slice.call([1,2,3], 0, 1); // => [1]

// Apply和Call差很少,知識參數要放在一個數組中
slice.apply([1,2,3], [0,1]); // => [1]

// 使用call沒錯了,那麼能不呢使用bind呢?
// 沒錯,咱們來把"call"綁定在slice上
slice = Function.prototype.call.bind(Array.prototype.slice);

// 如今slice能夠把第一個參數做爲上下文了
slice([1,2,3], 0, 1); // => [1]

//
// 很酷,對吧。如今再來完成一件事
//

// 如今咱們對bind自己作一件剛纔對silce作的事
var bind = Function.prototype.call.bind(Function.prototype.bind);

// 在這裏總結一下,好好想一想
// 發生了什麼事? 咱們改變了call,
// 返回一個接收一個函數和一個上下文做爲ic桉樹的函數
//而且返回了一個徹底綁定的函數

// 回到最初的例子
var context = { foo: "bar" };
function returnFoo () {
  return this.foo;
}

// 如今來使用神奇的"bind"函數
var amazing = bind(returnFoo, context);
amazing(); // => bar

    最後第三部分的代碼來自一段譯文:https://variadic.me/posts/2013-10-22-bind-call-and-apply-in-javascript.html,代碼很好因此忍不住拿來用,十分感謝.

    補充一段代碼,關於Ajax的回調中,如何保持this:

$.ajax({
      url: url,
      type: 'post',
      dataType: 'json',
      data: {'info': info}
    })
    .done((function(data) {
      if(data.status){
        // 這裏this指向的是外層bind進來的this
        this._data.process_type = info.process_type;
      }else{
        uUnique.noticeBox.showWarning(data.message);
      }
    }).bind(this));

    OK,感受說到這裏bind是作什麼的應該麼能夠理解了,最近打算寫寫如何在沒有MVC的庫的狀態下,寫出帶有MVC思想的代碼~GO.

相關文章
相關標籤/搜索