Ben Howdlejavascript
binding多是初學Javascript的人最不關心的函數,當你意識到須要『保持this在其餘函數中的上下文』,實際上你須要的是Function.prototype.bind()。html
你第一次碰到問題的時候,你可能傾向於把this賦值給一個變量,你就能夠在上下文改變的時候,也可使用。許多人選擇self,_this或者context來命名。這些都不會被用到,這樣作也沒什麼問題,可是這裏有更好的辦法,專門解決這個問題。java
我願意爲做用域作任何事,但我不會that = thisnode
— Jake Archibald (@jaffathecake) February 20, 2013瀏覽器
看看這段代碼,把上下文賦值給一個變量:安全
var myObj = { specialFunction: function () { }, anotherSpecialFunction: function () { }, getAsyncData: function (cb) { cb(); }, render: function () { var that = this; this.getAsyncData(function () { that.specialFunction(); that.anotherSpecialFunction(); }); } }; myObj.render();
若是上面直接用this.specialFunction(),結果是一個錯誤信息:app
Uncaught TypeError: Object [object global] has no method 'specialFunction'框架
當回調的時候,咱們須要保持myObj的上下文引用。使用that.specialFunction(),讓咱們用that的上下文且正確執行函數。然而,用Function.prototype.bind()能夠簡化一些。dom
重寫例子:函數
render: function () { this.getAsyncData(function () { this.specialFunction(); this.anotherSpecialFunction(); }.bind(this)); }
.bind()就是建立了一個新函數,當咱們呼叫時,把他的this賦值。因此咱們能夠傳遞咱們的上下文,this(指向myObj),傳遞進.bind()函數。當回調執行的時候,this指向myObj。
若是咱們對Function.prototype.bind()的內部實現有興致,請看下面的例子:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope); }; }
一個簡單的例子:
var foo = { x: 3 } var bar = function(){ console.log(this.x); } bar(); // undefined var boundFunc = bar.bind(foo); boundFunc(); // 3
Browser | Version support |
---|---|
Chrome | 7 |
Firefox (Gecko) | 4.0 (2) |
IE | 9 |
Opera | 11.60 |
Safari | 5.1.4 |
如你所見,不幸的是,不支持ie8如下(啥也不說了)。
幸運的是,MDN爲那些原生不支持.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, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; }
學習東西時候,我發現有效的方式不是認真的去學習概念,而是去看怎麼使用到如今的工做中。若是順利的話,下面某些例子能夠被用到你的代碼中解決你面對的問題。
其中一個用處是追蹤點擊(點擊後執行一個動做),須要咱們在一個對象中儲存信息:
var logger = { x: 0, updateCount: function(){ this.x++; console.log(this.x); } }
咱們寫click事件處理,而後呼叫logger中的updateCount():
document.querySelector('button').addEventListener('click',logger.updateCount);
但咱們造了一個沒必要要的匿名函數,保持this的正確指向。
簡化一下:
document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
剛纔咱們用了.bind()創造一個新函數而後把做用域綁定到logger對象上。
若是你之前用過模板引擎(handlebars)或者MV*框架,那你應該意識到一個問題的發生,當你呼叫渲染模板,馬上想進入新的DOM節點。
假設咱們嘗試實例一個jQuery插件:
var myView = { template: '/* a template string containing our <select /> */', $el: $('#content'), afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); this.afterRender(); } } myView.render();
你會發現這可用,但並不老是可用的。這就是問題所在。這就像是老鼠賽跑:無論發生什麼,第一個到達得到勝利。有時候是render,有時候是插件的實例(instantiation)。
目前,一個鮮爲人知,咱們能夠用小hack---setTimeout()。
須要重寫一下,一旦Dom節點出現,咱們就能夠安全的實例咱們的JQuery插件。
// afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender, 0); } //
但是,咱們會看到.afterRender()沒有被找到。
咋辦?把咱們.bind()加進去:
// afterRender: function () { this.$el.find('select').myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender.bind(this), 0); } //
如今afterRender()能夠在正確的上下文中執行了。
DOM API一個重大提升就是querySelector,querySelectorAll和classList API等等。
然而,並無原生添加事件到多個節點(nodeList)的方式。因此,咱們最終偷竊了forEach函數,來自Array.prototype,以下:
Array.prototype.forEach.call(document.querySelectorAll('.klasses'), function(el){ el.addEventListener('click', someFunction); });
更好一點,用.bind():
var unboundForEach = Array.prototype.forEach, forEach = Function.prototype.call.bind(unboundForEach); forEach(document.querySelectorAll('.klasses'), function (el) { el.addEventListener('click', someFunction); });
如今咱們有了小巧的方法來循環多個dom節點。
如你所見,.bind()函數能夠用來完成各類目的,同時簡化代碼。但願這個概述能讓你的代碼能使用.bind(),利用好變化的this這個特徵。
『能力有限,若有疑問,紕漏,速指出,感謝你』