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这个特征。
『能力有限,若有疑问,纰漏,速指出,感谢你』