javascript中的Function.prototye.bind
函數(shù)綁定(Function binding)很有可能是你在開(kāi)始使用JavaScript時(shí)最少關(guān)注的一點(diǎn),但是當(dāng)你意識(shí)到你需要一個(gè)解決方案來(lái)解決如何在另一個(gè)函數(shù)中保持this上下文的時(shí)候,你真正需要的其實(shí)就是 Function.prototype.bind(),只是你有可能仍然沒(méi)有意識(shí)到這點(diǎn)。
第一次遇到這個(gè)問(wèn)題的時(shí)候,你可能傾向于將this設(shè)置到一個(gè)變量上,這樣你可以在改變了上下文之后繼續(xù)引用到它。很多人選擇使用 self, _this 或者 context 作為變量名稱(也有人使用 that)。這些方式都是有用的,當(dāng)然也沒(méi)有什么問(wèn)題。但是其實(shí)有更好、更專用的方式。
我們真正需要解決的問(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();
如果我們簡(jiǎn)單地使用 this.specialFunction() 來(lái)調(diào)用方法的話,會(huì)收到下面的錯(cuò)誤:
Uncaught TypeError: Object [object global] has no method 'specialFunction'
我們需要為回調(diào)函數(shù)的執(zhí)行保持對(duì) myObj 對(duì)象上下文的引用。 調(diào)用 that.specialFunction()讓我們能夠維持作用域上下文并且正確執(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)這個(gè)函數(shù)在被調(diào)用的時(shí)候,它的 this 關(guān)鍵詞會(huì)被設(shè)置成被傳入的值(這里指調(diào)用bind()時(shí)傳入的參數(shù))。因此,我們傳入想要的上下文,this(其實(shí)就是 myObj),到.bind()函數(shù)中。然后,當(dāng)回調(diào)函數(shù)被執(zhí)行的時(shí)候, this 便指向 myObj 對(duì)象。
如果有興趣想知道 Function.prototype.bind() 內(nèi)部長(zhǎng)什么樣以及是如何工作的,這里有個(gè)非常簡(jiǎn)單的例子:
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
我們創(chuàng)建了一個(gè)新的函數(shù),當(dāng)它被執(zhí)行的時(shí)候,它的 this 會(huì)被設(shè)置成 foo —— 而不是像我們調(diào)用 bar() 時(shí)的全局作用域。
瀏覽器支持
Browser Version support
Chrome 7
Firefox (Gecko) 4.0 (2)
Internet Explorer 9
Opera 11.60
Safari 5.1.4
正如你看到的,很不幸,F(xiàn)unction.prototype.bind 在IE8及以下的版本中不被支持,所以如果你沒(méi)有一個(gè)備用方案的話,可能在運(yùn)行時(shí)會(huì)出現(xiàn)問(wèn)題。
幸運(yùn)的是,Mozilla Developer Network(很棒的資源庫(kù)),為沒(méi)有自身實(shí)現(xiàn) .bind() 方法的瀏覽器提供了一個(gè)絕對(duì)可靠的替代方案:
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ù)點(diǎn)的時(shí)候,我發(fā)現(xiàn)有用的不僅僅在于徹底學(xué)習(xí)和理解概念,更在于看看在手頭的工作中有沒(méi)有適用它的地方,或者比較接近它的的東西。我希望,下面的某些例子能夠適用于你的代碼或者解決你正在面對(duì)的問(wèn)題。
CLICK HANDLERS(點(diǎn)擊處理函數(shù))
一個(gè)用途是記錄點(diǎn)擊事件(或者在點(diǎn)擊之后執(zhí)行一個(gè)操作),這可能需要我們?cè)谝粋€(gè)對(duì)象中存入一些信息,比如:
var logger = {
x: 0,
updateCount: function(){
this.x++;
console.log(this.x);
}
}
我們可能會(huì)以下面的方式來(lái)指定點(diǎn)擊處理函數(shù),隨后調(diào)用 logger 對(duì)象中的 updateCount() 方法。
document.querySelector('button').addEventListener('click', function(){
logger.updateCount();
});
但是我們必須要?jiǎng)?chuàng)建一個(gè)多余的匿名函數(shù),來(lái)確保 updateCount()函數(shù)中的 this 關(guān)鍵字有正確的值。
我們可以使用如下更干凈的方式:
document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
我們巧妙地使用了方便的 .bind() 函數(shù)來(lái)創(chuàng)建一個(gè)新的函數(shù),而將它的作用域綁定為 logger 對(duì)象。
SETTIMEOUT
如果你使用過(guò)模板引擎(比如Handlebars)或者尤其使用過(guò)某些MV*框架(從我的經(jīng)驗(yàn)我只能談?wù)揃ackbone.js),那么你也許知道下面討論的關(guān)于在渲染模板之后立即訪問(wèn)新的DOM節(jié)點(diǎn)時(shí)會(huì)遇到的問(wèn)題。
假設(shè)我們想要實(shí)例化一個(gè)jQuery插件:
var myView = {
template: '/* 一個(gè)包含 <select /> 的模板字符串*/',
$el: $('#content'),
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
this.afterRender();
}
}
myView.render();
你或許發(fā)現(xiàn)它能正常工作——但并不是每次都行,因?yàn)槔锩娲嬖谥鴨?wèn)題。這是一個(gè)競(jìng)爭(zhēng)的問(wèn)題:只有先到達(dá)的才能獲勝。有時(shí)候是渲染先到,而有時(shí)候是插件的實(shí)例化先到?!咀g者注:如果渲染過(guò)程還沒(méi)有完成(DOM Node還沒(méi)有被添加到DOM樹(shù)上),那么find(‘select')將無(wú)法找到相應(yīng)的節(jié)點(diǎn)來(lái)執(zhí)行實(shí)例化。】
現(xiàn)在,或許并不被很多人知曉,我們可以使用基于 setTimeout() 的 slight hack來(lái)解決問(wèn)題。
我們稍微改寫(xiě)一下我們的代碼,就在DOM節(jié)點(diǎn)加載后再安全的實(shí)例化我們的jQuery插件:
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
setTimeout(this.afterRender, 0);
}
然而,我們獲得的是 函數(shù) .afterRender() 不能找到 的錯(cuò)誤信息。
我們接下來(lái)要做的,就是將.bind()使用到我們的代碼中:
//
afterRender: function () {
this.$el.find('select').myPlugin();
},
render: function () {
this.$el.html(this.template());
setTimeout(this.afterRender.bind(this), 0);
}
//
以上所述就是本文的全部?jī)?nèi)容了,希望大家能夠喜歡。
- 讓IE8瀏覽器支持function.bind()方法
- ie支持function.bind()方法實(shí)現(xiàn)代碼
- Function.prototype.bind用法示例
- 深入理解JS中的Function.prototype.bind()方法
- jQuery中的.bind()、.live()和.delegate()之間區(qū)別分析
- JQuery中綁定事件(bind())和移除事件(unbind())
- JQuery中Bind()事件用法分析
- jQuery事件綁定on()、bind()與delegate() 方法詳解
- 淺談javascript中call()、apply()、bind()的用法
- 關(guān)于Function中的bind()示例詳解
相關(guān)文章
深入理解JavaScript中async/await的錯(cuò)誤處理方式
在現(xiàn)代JavaScript開(kāi)發(fā)中,異步編程是不可或缺的一部分,async和await是一種強(qiáng)大的異步編程工具,它們使得編寫(xiě)和維護(hù)異步代碼更加容易和清晰,然而,異步操作仍然可能會(huì)出現(xiàn)錯(cuò)誤,本文將深入探討async和await的錯(cuò)誤處理方式,提供詳細(xì)的代碼示例和解釋2023-09-09
Bootstrap滾動(dòng)監(jiān)聽(tīng)組件scrollspy.js使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Bootstrap滾動(dòng)監(jiān)聽(tīng)組件scrollspy.js的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
Javascript BOM學(xué)習(xí)小結(jié)(六)
BOM:BrowserObjectModel,瀏覽器對(duì)象模型,提供JS中對(duì)瀏覽器的各種操作的對(duì)象,是JS應(yīng)用中唯一沒(méi)有相關(guān)標(biāo)準(zhǔn)的部分,這事BOM經(jīng)常出現(xiàn)問(wèn)題的所在,主要用于處理瀏覽器窗口與框架,瀏覽器特有的JS擴(kuò)展也被默認(rèn)為BOM的一部分,而各瀏覽器之間的公有對(duì)象就成了默認(rèn)的標(biāo)準(zhǔn)2015-11-11
js輸入框郵箱自動(dòng)提示功能代碼實(shí)現(xiàn)
一個(gè)輸入框當(dāng)我輸入任何字的時(shí)候自動(dòng)下拉相應(yīng)的郵箱提示,在輸入框輸入11的時(shí)候下拉框有所有11的郵箱 輸入其他的時(shí)候 有其他文案對(duì)應(yīng)的郵箱,下面實(shí)現(xiàn)這個(gè)自動(dòng)提示功能2013-12-12
保姆級(jí)Decimal.js使用教程(解決js精度問(wèn)題)
這篇文章主要給大家介紹了關(guān)于保姆級(jí)Decimal.js使用的相關(guān)資料,文中介紹的方法主要解決了js精度問(wèn)題,decimal.js庫(kù)它是一個(gè)強(qiáng)大的JavaScript庫(kù),用于解決浮點(diǎn)數(shù)計(jì)算精度不準(zhǔn)確的問(wèn)題,需要的朋友可以參考下2024-05-05
JavaScript實(shí)現(xiàn)無(wú)限級(jí)遞歸樹(shù)的示例代碼
這篇文章主要介紹了JavaScript實(shí)現(xiàn)無(wú)限級(jí)遞歸樹(shù)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03

