bind() 和 箭頭函數(shù)的this

箭頭函數(shù)的This指向

更新 2017年11月26號(hào)


前言

樓主在昨天在看Vue文檔的時(shí)候腿准,主要到methodscomputed里面不要使用箭頭函數(shù),去看了下源碼解析拾碌,發(fā)現(xiàn)里面調(diào)用的是通過自定義的bind函數(shù)吐葱,通過call()來執(zhí)行函數(shù)以及綁定作用域,想鞏固一下箭頭函數(shù)校翔,于是這篇有內(nèi)涵的blog就上線了弟跑。

之前樓主有一篇箭頭函數(shù)的This, 對(duì)于它的理解感覺有偏差,這里全部再重復(fù)總結(jié)一遍防症。

看完本篇文章孟辑,你可以徹底了解this和bind

涉及知識(shí)點(diǎn)

  1. bind函數(shù)的深入了解解析
  2. 作用域
  3. thisArg

實(shí)現(xiàn)一個(gè)bind函數(shù)

var fn = function(a,b,c,d) {
  return a+b+c+d ;
}
fn.bind(scope,a,b)(c,d)
// 調(diào)用方式  var fn1 = fn.bind(scope,a,b)  fn1(c, d)
// scope是傳遞進(jìn)來的this
Function.prototype.bind = function(scope) {
  let newFn = this;
  // 獲取通過bind傳遞的參數(shù)
  let args = Array.prototype.slice.call(arguments,1)
  let fbind = function() {
    // 這里執(zhí)行函數(shù)奈嘿,通過閉包綁定了this===scope
    // 然后通過concat合并2個(gè)參數(shù)
    return newFn.apply(scope,args.concat(Array.prototype.slice.call(arguments)))
  }
  return fbind
}

調(diào)用方式:

  1. 直接調(diào)用 (內(nèi)部this一般是window)
  1. 構(gòu)造函數(shù)的調(diào)用袄膏,通過bind綁定的this無效 (this是實(shí)例對(duì)象)

當(dāng)使用構(gòu)造函數(shù)的時(shí)候,我們需要構(gòu)建原型鏈心赶,所以需要加工一下荔燎。

Function.prototype.bind = function(scope) {
  if( typeof this !== 'function') {
    throw('this is illegal')
  }
  let newFn = this;
  // 獲取通過bind傳遞的參數(shù)
  let args = Array.prototype.slice.call(arguments,1)
  let fbind = function() {
    // 里面的this可能是window和構(gòu)造函數(shù)實(shí)例
    // 然后通過concat合并2個(gè)參數(shù)
    return newFn.apply(this instanceof fbind ? this : scope              ,args.concat(Array.prototype.slice.call(arguments)))
  }
  // 維持原型鏈
  if(this.prototype) {
    fbind.prototype = this.prototype
  }
  return fbind
}
// 
function Fn(a,b) {
  this.a = a
  this.b = b
}

Fn.prototype = function() { console.log(this)}
let hcc = Fn.bind({a:1})
// 直接調(diào)用
hcc()  // 返回一個(gè)新函數(shù)fbind,  fbind里面的this則是window
// 所以 this instanceof fbind 為 false 

// 構(gòu)造函數(shù)的調(diào)用
let obj = new hcc() // fbind里面的this則是fbind的實(shí)例
// 所以 this instanceof fbind 為 true ,所以改變this沒有生效 

解析 : 當(dāng)我們調(diào)用bind()的時(shí)候婉商,即執(zhí)行了var fn1 = fn.bind({name: 1},1,3), 會(huì)返回一個(gè)新的函數(shù),下面是作用域鏈解析筷狼。

實(shí)例 : 當(dāng)我們調(diào)用bind()的時(shí)候俏险,即執(zhí)行了var fn1 = fn.bind({name: 1},1,3), 會(huì)返回一個(gè)新的函數(shù),下面是作用域鏈解析航瞭。

bind => function(scope) {
  let newFn = fn;
  let args = [1,3]
  let fbind = function() {
    return newFn.apply(scope,[1,3].concat(Array.prototype.slice.call(arguments)))
  }
  return fbind
}

fn1 =>  function() {
    return fn.apply({name: 1},[1,3].concat(Array.prototype.slice.call(arguments)))
}

再調(diào)用fn1(3,4),相當(dāng)于執(zhí)行函數(shù)

 fn.apply({name: 1},[1,3].concat(Array.prototype.slice.call(arguments)))
 // 即相當(dāng)于這樣執(zhí)行
 fn.apply({name: 1},[1,3,3,4]) =>  11

思考,下面函數(shù)執(zhí)行時(shí)多少锉走,this是什么

var fn = function(a,b) {
  console.log(this)
  return a+b ;
}
fn.bind({name:1},1,2).call({name:2},3,4)  

答案 {name:1} 3

//fn.bind({name:1},1,2)  返回xxx
function xxx() {
   return fn.apply({name: 1},[1,2].concat(Array.prototype.slice.call(arguments)))
} 

// xxx.call({name:2},3,4) 調(diào)用 xxx(綁定了xxx的this= {name:2})
// xxx里面通過apply調(diào)用已經(jīng)制定了this的fn函數(shù)
 fn.apply({name: 1},[1,2,3,4]) // this => {name: 1} a=1, b=2 

所以當(dāng)我們執(zhí)行fn.bind({name:1},1,2).call({name:2},3,4) 梁厉,本質(zhì)上call并不能改變bind的返回函數(shù)的this,只是改變了內(nèi)部封裝了一個(gè)函數(shù)(xxx)的this,這也是bind的this參數(shù)不能被重寫的原因。

總結(jié)bind函數(shù)到底做了什么

fun.bind(thisArg[, arg1[, arg2[, ...]]])
// 簡(jiǎn)化版
Function.prototype.bind = function bind(self) {
  return function() { return fn.apply(self) }
}

當(dāng)一個(gè)函數(shù)(fn)使用函數(shù)原型鏈上面的bind函數(shù)的時(shí)候,傳遞this(thisArg)和參數(shù)進(jìn)去,返回的是一個(gè)新函數(shù)(xxx)混移,新函數(shù)內(nèi)部調(diào)用的是通過apply調(diào)用原來的函數(shù)(fn)并制定原函數(shù)(fn)的this亲茅。用簡(jiǎn)單的代碼表示就是:

function fn(a,b) {
  return a+b;
}
fn.bind({name:1},1,3)  相當(dāng)于變成這樣=> function xxx() {
  return fn.apply({name: 1},Array.prototype.slice.call(arguments));
}

箭頭函數(shù)的this(定義時(shí)候的this)

一句話總結(jié): 箭頭函數(shù)的函數(shù)體內(nèi)的this就是定義時(shí)候的this腔长,和使用所在的this沒有關(guān)系胚膊。

:在定義箭頭函數(shù)的時(shí)候就已經(jīng)綁定了this辑舷,可以理解為就是在定義的時(shí)候碌廓,通過bind函數(shù)進(jìn)行強(qiáng)行綁定this。

案例一

var calculate = {
  array: [1, 2, 3],
  sum:() => {
    console.log(this === window) // => true
  }
};
// 當(dāng)我們?cè)诙xsum是一個(gè)箭頭函數(shù)的時(shí)候异袄,還沒有執(zhí)行,內(nèi)部已經(jīng)綁定了this,而此時(shí)的this就是全局的window

//可以轉(zhuǎn)換成
var calculate = {
  array: [1, 2, 3],
  sum:function() {
    console.log(this === window) // => true
  }.bind(this)
};

案例二

var calculate = {
  array: [1, 2, 3],
  sum() => {
    return () => {
      console.log(this === window) 
    }
  }
};
// 此時(shí)我們?cè)趯慶alculate.sum的時(shí)候呐籽,由于還沒有執(zhí)行,所有并不存在里面的箭頭函數(shù),當(dāng)我們執(zhí)行calculate.sum()才算生成了箭頭函數(shù),箭頭函數(shù)就是在這個(gè)時(shí)候綁定this的并淋,所有這里就會(huì)和怎么調(diào)用sum函數(shù)有關(guān)系了。

案例三

const App = new Vue({
    el: '#app',
    methods: {
        foo: () => {
            console.log(this) // undefined
        }
    }
})
// 如果我們?cè)赩ue的實(shí)例中的methods使用箭頭函數(shù)兔毙,那么在定義的時(shí)候神郊,箭頭函數(shù)會(huì)自動(dòng)綁定當(dāng)前作用域的this,并不會(huì)是綁定實(shí)例中的this
// 初始化的時(shí)候,執(zhí)行的initMethods中綁定了this(vm)
function initMethods (vm: Component) {
  const methods = vm.$options.methods
  if (methods) {
    for (const key in methods) {
      vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
      if (process.env.NODE_ENV !== 'production' && methods[key] == null) {
        warn(
          `method "${key}" has an undefined value in the component definition. ` +
          `Did you reference the function correctly?`,
          vm
        )
      }
    }
  }
}

//bind

function bind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)//通過返回函數(shù)修飾了事件的回調(diào)函數(shù)躬贡。綁定了事件回調(diào)函數(shù)的this酸些。并且讓參數(shù)自定義缀拭。更加的靈活
        : fn.call(ctx, a)
      : fn.call(ctx)
  }
  // record original fn length
  boundFn._length = fn.length;
  return boundFn
}

總結(jié):我們知道Vue內(nèi)部調(diào)用methods的時(shí)候蛛淋,通過的call方法來執(zhí)行methods中的相應(yīng)的key函數(shù)敷扫,當(dāng)我們使用箭頭函數(shù)的時(shí)候缀台,定義的時(shí)候就綁定了this,它源碼中寫的call()并不會(huì)被使用脯丝,所以必須不能使用箭頭函數(shù)

Vue文檔中methods的使用


參考文章

Vue methods 用箭頭函數(shù)取不到 this

vue源碼解析-事件機(jī)制

什么時(shí)候“不要”用箭頭函數(shù)

ES6 箭頭函數(shù)使用禁忌

推理例子

自己寫的推理例子

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茬缩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吼旧,更是在濱河造成了極大的恐慌凰锡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圈暗,死亡現(xiàn)場(chǎng)離奇詭異掂为,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)员串,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門勇哗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寸齐,你說我怎么就攤上這事欲诺。” “怎么了渺鹦?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵扰法,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我毅厚,道長(zhǎng)塞颁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任吸耿,我火速辦了婚禮祠锣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咽安。我一直安慰自己伴网,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布板乙。 她就那樣靜靜地躺著是偷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪募逞。 梳的紋絲不亂的頭發(fā)上蛋铆,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音放接,去河邊找鬼刺啦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛纠脾,可吹牛的內(nèi)容都是我干的玛瘸。 我是一名探鬼主播蜕青,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼糊渊!你這毒婦竟也來了右核?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渺绒,失蹤者是張志新(化名)和其女友劉穎贺喝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宗兼,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躏鱼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了殷绍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片染苛。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖主到,靈堂內(nèi)的尸體忽然破棺而出茶行,到底是詐尸還是另有隱情,我是刑警寧澤镰烧,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布拢军,位于F島的核電站,受9級(jí)特大地震影響怔鳖,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜固蛾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一结执、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧艾凯,春花似錦献幔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至恃泪,卻和暖如春郑兴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贝乎。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工情连, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人览效。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓却舀,卻偏偏與公主長(zhǎng)得像虫几,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挽拔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345