bind 簡(jiǎn)介

原文出處

Ok遣铝,今天要講的正是 bind棱貌。關(guān)于 bind,可以先移步樓主以前寫(xiě)的文章 ECMAScript 5(ES5) 中 bind 方法簡(jiǎn)介備忘贞间,有個(gè)基本的概念。

bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)雹仿,當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí)增热,它的 this 值是傳遞給 bind() 的第一個(gè)參數(shù), 它的參數(shù)是 bind() 的其他參數(shù)和其原本的參數(shù)。

語(yǔ)法是這樣樣子的:

fun.bind(thisArg[, arg1[, arg2[, ...]]])

  • thisArg 當(dāng)綁定函數(shù)被調(diào)用時(shí)胧辽,該參數(shù)會(huì)作為原函數(shù)運(yùn)行時(shí)的 this 指向峻仇。當(dāng)使用 new 操作符調(diào)用綁定函數(shù)時(shí),該參數(shù)無(wú)效邑商。
  • arg1, arg2, ... (可選)當(dāng)綁定函數(shù)被調(diào)用時(shí)摄咆,這些參數(shù)加上綁定函數(shù)本身的參數(shù)會(huì)按照順序作為原函數(shù)運(yùn)行時(shí)的參數(shù)。

參數(shù)

bind 的第一個(gè)參數(shù)會(huì)作為原函數(shù)運(yùn)行時(shí)的 this 指向人断,不多說(shuō)吭从;而第二個(gè)開(kāi)始的參數(shù)是可選的,當(dāng)綁定函數(shù)被調(diào)用時(shí)含鳞,這些參數(shù)加上綁定函數(shù)本身的參數(shù)會(huì)按照順序作為原函數(shù)運(yùn)行時(shí)的參數(shù)影锈。怎么理解?

function fn(a, b, c) {
  return a + b + c;
}

var _fn = fn.bind(null, 10);
var ans = _fn(20, 30); // 60

fn 函數(shù)需要三個(gè)參數(shù),_fn 函數(shù)將 10 作為默認(rèn)的第一個(gè)參數(shù)鸭廷,所以只需要傳入兩個(gè)參數(shù)即可枣抱,如果你不小心傳入了三個(gè)參數(shù),放心辆床,也只會(huì)取前兩個(gè)佳晶。

function fn(a, b, c) {
  return a + b + c;
}

var _fn = fn.bind(null, 10);
var ans = _fn(20, 30, 40); // 60

這有啥用呢?如果某些函數(shù)讼载,前幾個(gè)參數(shù)已經(jīng) “內(nèi)定” 了轿秧,我們便可以用 bind 返回一個(gè)新的函數(shù)。也就是說(shuō)咨堤,bind() 能使一個(gè)函數(shù)擁有預(yù)設(shè)的初始參數(shù)菇篡。這些參數(shù)(如果有的話)作為 bind() 的第二個(gè)參數(shù)跟在 this 后面,之后它們會(huì)被插入到目標(biāo)函數(shù)的參數(shù)列表的開(kāi)始位置一喘,傳遞給綁定函數(shù)的參數(shù)會(huì)跟在它們的后面驱还。

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

new

使用 bind 返回的結(jié)果還是個(gè) function,是個(gè) function 就可以被 new 運(yùn)算符調(diào)用凸克,那么結(jié)果呢议蟆?規(guī)范中說(shuō)的很清楚了,當(dāng)使用 new 操作符調(diào)用綁定函數(shù)時(shí)萎战,bind 的第一個(gè)參數(shù)無(wú)效咐容。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

var _Person = Person.bind({});
var p = new _Person('hanzichi', 30); // Person {name: "hanzichi", age: 30}

一般我們不會(huì)去這么用,但是如果要寫(xiě)個(gè) bind 的 polyfill(http://caniuse.com/#search=bind)蚂维,還是需要考慮用 new 調(diào)用的情況戳粒。

我們也可以設(shè)置默認(rèn)值(參考上一小節(jié)),原先提供的那些參數(shù)仍然會(huì)被前置到構(gòu)造函數(shù)調(diào)用的前面鸟雏。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

var _Person = Person.bind(null, 'hanzichi');
var p = new _Person(30); // Person {name: "hanzichi", age: 30}

配合 setTimeout

什么時(shí)候容易丟失 this 指向享郊?恩,setTimeout 是一個(gè)場(chǎng)景孝鹊,很容易把 this 指向 window炊琉,當(dāng)然,setInterval 也是一樣又活。當(dāng)使用對(duì)象的方法時(shí)苔咪,需要 this 引用對(duì)象,你可能需要顯式地把 this 綁定到回調(diào)函數(shù)以便繼續(xù)使用對(duì)象柳骄。

var canvas = {
  render: function() {
    this.update();
    this.draw();
  },

  update: function() {
    // ...
  },

  draw: function() {
    // ...
  }
};

window.setInterval(canvas.render, 1000 / 60);

用 canvas 寫(xiě)特效或者做游戲時(shí)經(jīng)常會(huì)碰到類似的問(wèn)題团赏。上面的代碼是有問(wèn)題的,render 方法中的 this 其實(shí)被指向了 window耐薯!我們可以用 bind舔清,顯式地把 this 綁定到回調(diào)函數(shù)以便繼續(xù)使用該對(duì)象丝里。

window.setInterval(canvas.render.bind(canvas), 1000);

類似的情況還有 dom 的事件監(jiān)聽(tīng),一不小心可能 this 就被指向了 dom 元素体谒”郏可以參考下以前做 bigrender 時(shí)寫(xiě)的這部分代碼 https://github.com/hanzichi/hanzichi.github.io/blob/master/2016/bigrender/js/bigrender.js#L179-L184。

tip

bind 還能做一些有意思的事情抒痒。

通常來(lái)說(shuō)幌绍,將一個(gè)類數(shù)組轉(zhuǎn)為數(shù)組,我們會(huì)用 slice(ie9- 不支持)故响。參考 #14

var slice = Array.prototype.slice;

// slice.apply(arguments);
// slice.call(arguments, 1);

bind 能讓調(diào)用變的更加簡(jiǎn)單傀广。

// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

// ...

slice(arguments);
// slice(arguments, 1);

再舉個(gè)類似的例子,比如說(shuō)我們要添加事件到多個(gè)節(jié)點(diǎn)彩届,for 循環(huán)當(dāng)然沒(méi)有任何問(wèn)題伪冰,我們還可以 “剽竊” forEach 方法:

Array.prototype.forEach.call(document.querySelectorAll('input[type="button"]'), function(el){
  el.addEventListener('click', fn);
});

更進(jìn)一步,我們可以用 bind 將函數(shù)封裝的更好:

var unboundForEach = Array.prototype.forEach
  , forEach = Function.prototype.call.bind(unboundForEach);

forEach(document.querySelectorAll('input[type="button"]'), function (el) {
  el.addEventListener('click', fn);
});

同樣類似的惨缆,我們可以將 x.y(z) 變成 y(x,z) 的形式:

var obj = {
  num: 10,
  getCount: function() {
    return this.num;
  }
};

var unboundBind = Function.prototype.bind
  , bind = Function.prototype.call.bind(unboundBind);

var getCount = bind(obj.getCount, obj);
console.log(getCount());  // 10

再舉個(gè)栗子糜值。每隔一秒在控制臺(tái)打印 1-5,看起來(lái)是道考察閉包的經(jīng)典題目坯墨。

for(var i = 1; i <= 5; i++) {
  !function(i) {
    setTimeout(function() {
      console.log(i);
    }, i * 1000);
  }(i);
}

ES6 下能用 let

for(let i = 1; i <= 5; i++) {
  setTimeout(function() {
    console.log(i);
  }, i * 1000);
}

也可以用 bind,瞬間逼格提升:

for(var i = 1; i <= 5; i++) {
  setTimeout(console.log.bind(console, i), i * 1000);
}

2016-08-30 add:好吧病往,setTimeout 還能傳入第三個(gè)參數(shù)捣染,又漲姿勢(shì)了 ...

for (var i = 1; i <= 5; i++) {
  setTimeout(function(num) {
    console.log(num)
  }, i * 1000, i);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市停巷,隨后出現(xiàn)的幾起案子耍攘,更是在濱河造成了極大的恐慌,老刑警劉巖畔勤,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕾各,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庆揪,警方通過(guò)查閱死者的電腦和手機(jī)式曲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缸榛,“玉大人吝羞,你說(shuō)我怎么就攤上這事∧诳牛” “怎么了钧排?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)均澳。 經(jīng)常有香客問(wèn)我恨溜,道長(zhǎng)符衔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任糟袁,我火速辦了婚禮判族,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘系吭。我一直安慰自己五嫂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布肯尺。 她就那樣靜靜地躺著沃缘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪则吟。 梳的紋絲不亂的頭發(fā)上槐臀,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音氓仲,去河邊找鬼水慨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛敬扛,可吹牛的內(nèi)容都是我干的晰洒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼啥箭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谍珊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起急侥,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤砌滞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坏怪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贝润,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年铝宵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了打掘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捉超,死狀恐怖胧卤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拼岳,我是刑警寧澤枝誊,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站惜纸,受9級(jí)特大地震影響叶撒,放射性物質(zhì)發(fā)生泄漏绝骚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一祠够、第九天 我趴在偏房一處隱蔽的房頂上張望压汪。 院中可真熱鬧,春花似錦古瓤、人聲如沸止剖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)穿香。三九已至,卻和暖如春绎速,著一層夾襖步出監(jiān)牢的瞬間皮获,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工纹冤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洒宝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓萌京,卻偏偏與公主長(zhǎng)得像雁歌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子知残,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345