摘要:搞懂的译可能是初学的人最不关心的函数,当你意识到需要保持在其他函数中的上下文,实际上你需要的是。这就是问题所在。整合事件绑定和一个重大提高就是,和等等。然而,并没有原生添加事件到多个节点的方式。能力有限,如有疑问,纰漏,速指出,感谢你
搞懂JavaScript的Function.prototype.bind[译]
Ben Howdle
binding可能是初学Javascript的人最不关心的函数,当你意识到需要『保持this在其他函数中的上下文』,实际上你需要的是Function.prototype.bind()。
你第一次碰到问题的时候,你可能倾向于把this赋值给一个变量,你就可以在上下文改变的时候,也可以使用。许多人选择self,_this或者context来命名。这些都不会被用到,这样做也没什么问题,但是这里有更好的办法,专门解决这个问题。
我愿意为作用域做任何事,但我不会that = this
— 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(),结果是一个错误信息:
Uncaught TypeError: Object [object global] has no method "specialFunction"
当回调的时候,我们需要保持myObj的上下文引用。使用that.specialFunction(),让我们用that的上下文且正确执行函数。然而,用Function.prototype.bind()可以简化一些。
重写例子:
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 */", $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()可以在正确的上下文中执行了。
整合事件绑定和QUERYSELECTORALLDOM 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这个特征。
『能力有限,如有疑问,纰漏,速指出,感谢你』
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78947.html
摘要:虽然方法定义在对象里面,但是使用方法后,将方法里面的指向了。本文都是在非严格模式下的情况。在构造函数内部的内的回调函数,始终指向实例化的对象,并获取实例化对象的的属性每这个属性的值都会增加。否则最后在后执行函数执行后输出的是 本篇文章主要针对搞不清this指向的的同学们!不定期更新文章都是我学习过程中积累下的经验,还请大家多多关注我的文章以帮助更多的同学,不对的地方还望留言支持改进! ...
摘要:每个原型都有一个属性指向关联的构造函数由于实例对象可以继承原型对象的属性,所以实例对象也拥有属性,同样指向原型对象对应的构造函数。 showImg(https://segmentfault.com/img/remote/1460000017183104?w=1300&h=834); 构造函数:function Foo ( ) { }; 实例对象:let f1=new Foo; 谈到...
摘要:在我们实际使用中经常用的箭头函数来代替提取对象的方法如果将一个对象的方法作为回调函数传入你需要定义一个确定的否则它将作为一个函数来执行值可能是也可能是全局对象例如另一种解决方案就是使用箭头函数译者注原文评论中也提到了的绑定运算符如下作为参数 在我们实际使用中,经常用ES6的箭头函数来代替Function.prototype.bind(). 1.提取对象的方法 如果将一个对象的方法作为回...
摘要:但有些时候我们可能需要知道现在某个到底是否为运行时环境所原生支持,还是代码支持的。今天在学习版本的源代码时,就发现了中也有用来检测一个函数是否为运行时原生支持。 在开发过程中,对于某些API在现有的JavaScript运行时环境不支持的时候,我们大都会采用加入polyfill来解决这个问题。但有些时候我们可能需要知道现在某个API到底是否为运行时环境所原生支持,还是polyfill代码...
阅读 1476·2023-04-25 15:50
阅读 1283·2021-09-22 15:49
阅读 2891·2021-09-22 15:06
阅读 3487·2019-08-30 15:54
阅读 2286·2019-08-29 11:33
阅读 2094·2019-08-23 17:56
阅读 2106·2019-08-23 17:06
阅读 1265·2019-08-23 15:55