摘要:搞懂的譯可能是初學(xué)的人最不關(guān)心的函數(shù),當(dāng)你意識(shí)到需要保持在其他函數(shù)中的上下文,實(shí)際上你需要的是。這就是問(wèn)題所在。整合事件綁定和一個(gè)重大提高就是,和等等。然而,并沒(méi)有原生添加事件到多個(gè)節(jié)點(diǎn)的方式。能力有限,如有疑問(wèn),紕漏,速指出,感謝你
搞懂JavaScript的Function.prototype.bind[譯]
Ben Howdle
binding可能是初學(xué)Javascript的人最不關(guān)心的函數(shù),當(dāng)你意識(shí)到需要『保持this在其他函數(shù)中的上下文』,實(shí)際上你需要的是Function.prototype.bind()。
你第一次碰到問(wèn)題的時(shí)候,你可能傾向于把this賦值給一個(gè)變量,你就可以在上下文改變的時(shí)候,也可以使用。許多人選擇self,_this或者context來(lái)命名。這些都不會(huì)被用到,這樣做也沒(méi)什么問(wèn)題,但是這里有更好的辦法,專門解決這個(gè)問(wèn)題。
我愿意為作用域做任何事,但我不會(huì)that = this
— Jake Archibald (@jaffathecake) February 20, 2013
我們真正在尋求解決的問(wèn)題是什么?看看這段代碼,把上下文賦值給一個(gè)變量:
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(),結(jié)果是一個(gè)錯(cuò)誤信息:
Uncaught TypeError: Object [object global] has no method "specialFunction"
當(dāng)回調(diào)的時(shí)候,我們需要保持myObj的上下文引用。使用that.specialFunction(),讓我們用that的上下文且正確執(zhí)行函數(shù)。然而,用Function.prototype.bind()可以簡(jiǎn)化一些。
重寫例子:
render: function () { this.getAsyncData(function () { this.specialFunction(); this.anotherSpecialFunction(); }.bind(this)); }我們剛做了什么?
.bind()就是創(chuàng)建了一個(gè)新函數(shù),當(dāng)我們呼叫時(shí),把他的this賦值。所以我們可以傳遞我們的上下文,this(指向myObj),傳遞進(jìn).bind()函數(shù)。當(dāng)回調(diào)執(zhí)行的時(shí)候,this指向myObj。
如果我們對(duì)Function.prototype.bind()的內(nèi)部實(shí)現(xiàn)有興致,請(qǐng)看下面的例子:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope); }; }
一個(gè)簡(jiǎn)單的例子:
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 |
如你所見(jiàn),不幸的是,不支持ie8以下(啥也不說(shuō)了)。
幸運(yùn)的是,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; }; }使用方式
學(xué)習(xí)東西時(shí)候,我發(fā)現(xiàn)有效的方式不是認(rèn)真的去學(xué)習(xí)概念,而是去看怎么使用到現(xiàn)在的工作中。如果順利的話,下面某些例子可以被用到你的代碼中解決你面對(duì)的問(wèn)題。
點(diǎn)擊事件處理其中一個(gè)用處是追蹤點(diǎn)擊(點(diǎn)擊后執(zhí)行一個(gè)動(dòng)作),需要我們?cè)谝粋€(gè)對(duì)象中儲(chǔ)存信息:
var logger = { x: 0, updateCount: function(){ this.x++; console.log(this.x); } }
我們寫click事件處理,然后呼叫l(wèi)ogger中的updateCount():
document.querySelector("button").addEventListener("click",logger.updateCount);
但我們?cè)炝艘粋€(gè)不必要的匿名函數(shù),保持this的正確指向。
簡(jiǎn)化一下:
document.querySelector("button").addEventListener("click", logger.updateCount.bind(logger));
剛才我們用了.bind()創(chuàng)造一個(gè)新函數(shù)然后把作用域綁定到logger對(duì)象上。
時(shí)間間隔函數(shù)如果你以前用過(guò)模板引擎(handlebars)或者M(jìn)V*框架,那你應(yīng)該意識(shí)到一個(gè)問(wèn)題的發(fā)生,當(dāng)你呼叫渲染模板,立刻想進(jìn)入新的DOM節(jié)點(diǎn)。
假設(shè)我們嘗試實(shí)例一個(gè)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();
你會(huì)發(fā)現(xiàn)這可用,但并不總是可用的。這就是問(wèn)題所在。這就像是老鼠賽跑:不管發(fā)生什么,第一個(gè)到達(dá)獲得勝利。有時(shí)候是render,有時(shí)候是插件的實(shí)例(instantiation)。
目前,一個(gè)不為人知,我們可以用小hack---setTimeout()。
需要重寫一下,一旦Dom節(jié)點(diǎn)出現(xiàn),我們就可以安全的實(shí)例我們的JQuery插件。
// afterRender: function () { this.$el.find("select").myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender, 0); } //
可是,我們會(huì)看到.afterRender()沒(méi)有被找到。
咋辦?把我們.bind()加進(jìn)去:
// afterRender: function () { this.$el.find("select").myPlugin(); }, render: function () { this.$el.html(this.template()); setTimeout(this.afterRender.bind(this), 0); } //
現(xiàn)在afterRender()可以在正確的上下文中執(zhí)行了。
整合事件綁定和QUERYSELECTORALLDOM API一個(gè)重大提高就是querySelector,querySelectorAll和classList API等等。
然而,并沒(méi)有原生添加事件到多個(gè)節(jié)點(diǎn)(nodeList)的方式。所以,我們最終偷竊了forEach函數(shù),來(lái)自Array.prototype,如下:
Array.prototype.forEach.call(document.querySelectorAll(".klasses"), function(el){ el.addEventListener("click", someFunction); });
更好一點(diǎn),用.bind():
var unboundForEach = Array.prototype.forEach, forEach = Function.prototype.call.bind(unboundForEach); forEach(document.querySelectorAll(".klasses"), function (el) { el.addEventListener("click", someFunction); });
現(xiàn)在我們有了小巧的方法來(lái)循環(huán)多個(gè)dom節(jié)點(diǎn)。
總結(jié)如你所見(jiàn),.bind()函數(shù)可以用來(lái)完成各種目的,同時(shí)簡(jiǎn)化代碼。希望這個(gè)概述能讓你的代碼能使用.bind(),利用好變化的this這個(gè)特征。
『能力有限,如有疑問(wèn),紕漏,速指出,感謝你』
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78947.html
摘要:雖然方法定義在對(duì)象里面,但是使用方法后,將方法里面的指向了。本文都是在非嚴(yán)格模式下的情況。在構(gòu)造函數(shù)內(nèi)部的內(nèi)的回調(diào)函數(shù),始終指向?qū)嵗膶?duì)象,并獲取實(shí)例化對(duì)象的的屬性每這個(gè)屬性的值都會(huì)增加。否則最后在后執(zhí)行函數(shù)執(zhí)行后輸出的是 本篇文章主要針對(duì)搞不清this指向的的同學(xué)們!不定期更新文章都是我學(xué)習(xí)過(guò)程中積累下的經(jīng)驗(yàn),還請(qǐng)大家多多關(guān)注我的文章以幫助更多的同學(xué),不對(duì)的地方還望留言支持改進(jìn)! ...
摘要:每個(gè)原型都有一個(gè)屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)由于實(shí)例對(duì)象可以繼承原型對(duì)象的屬性,所以實(shí)例對(duì)象也擁有屬性,同樣指向原型對(duì)象對(duì)應(yīng)的構(gòu)造函數(shù)。 showImg(https://segmentfault.com/img/remote/1460000017183104?w=1300&h=834); 構(gòu)造函數(shù):function Foo ( ) { }; 實(shí)例對(duì)象:let f1=new Foo; 談到...
摘要:在我們實(shí)際使用中經(jīng)常用的箭頭函數(shù)來(lái)代替提取對(duì)象的方法如果將一個(gè)對(duì)象的方法作為回調(diào)函數(shù)傳入你需要定義一個(gè)確定的否則它將作為一個(gè)函數(shù)來(lái)執(zhí)行值可能是也可能是全局對(duì)象例如另一種解決方案就是使用箭頭函數(shù)譯者注原文評(píng)論中也提到了的綁定運(yùn)算符如下作為參數(shù) 在我們實(shí)際使用中,經(jīng)常用ES6的箭頭函數(shù)來(lái)代替Function.prototype.bind(). 1.提取對(duì)象的方法 如果將一個(gè)對(duì)象的方法作為回...
摘要:但是有一個(gè)總的原則,那就是指的是,調(diào)用函數(shù)的那個(gè)對(duì)象作為函數(shù)調(diào)用在函數(shù)被直接調(diào)用時(shí)綁定到全局對(duì)象。這一過(guò)程分為三步創(chuàng)建類的實(shí)例。這步是把一個(gè)空的對(duì)象的屬性設(shè)置為。函數(shù)被傳入?yún)?shù)并調(diào)用,關(guān)鍵字被設(shè)定為該實(shí)例。 this 的值到底是什么?一次說(shuō)清楚你怎么還沒(méi)搞懂 this?this、apply、call、bind 一、Whats this? 由于運(yùn)行期綁定的特性,JavaScript 中的...
摘要:但有些時(shí)候我們可能需要知道現(xiàn)在某個(gè)到底是否為運(yùn)行時(shí)環(huán)境所原生支持,還是代碼支持的。今天在學(xué)習(xí)版本的源代碼時(shí),就發(fā)現(xiàn)了中也有用來(lái)檢測(cè)一個(gè)函數(shù)是否為運(yùn)行時(shí)原生支持。 在開(kāi)發(fā)過(guò)程中,對(duì)于某些API在現(xiàn)有的JavaScript運(yùn)行時(shí)環(huán)境不支持的時(shí)候,我們大都會(huì)采用加入polyfill來(lái)解決這個(gè)問(wèn)題。但有些時(shí)候我們可能需要知道現(xiàn)在某個(gè)API到底是否為運(yùn)行時(shí)環(huán)境所原生支持,還是polyfill代碼...
閱讀 1608·2023-04-25 15:50
閱讀 1318·2021-09-22 15:49
閱讀 2946·2021-09-22 15:06
閱讀 3610·2019-08-30 15:54
閱讀 2345·2019-08-29 11:33
閱讀 2129·2019-08-23 17:56
閱讀 2160·2019-08-23 17:06
閱讀 1306·2019-08-23 15:55