call、apply找都、bind

call唇辨、apply、bind的作用是改變函數(shù)運(yùn)行時this的指向能耻,所以先說清楚this赏枚。

方法調(diào)用模式

當(dāng)一個函數(shù)被保存為對象的一個方法時,如果調(diào)用表達(dá)式包含一個提取屬性的動作晓猛,那么它就是被當(dāng)做一個方法來調(diào)用饿幅,此時的this被綁定到這個對象。

 var a = 1
    var obj1 = {
      a:2,
      fn:function(){
        console.log(this.a)
      }
    }
    obj1.fn()//2  

DOM對象綁定事件也屬于方法調(diào)用模式戒职,因此它綁定的this就是事件源DOM對象诫睬。

document.addEventListener('click', function(e){
    console.log(this);
    setTimeout(function(){
        console.log(this);
    }, 200);
}, false);

點(diǎn)擊頁面,依次輸出:document和window對象帕涌。

函數(shù)調(diào)用模式:

就是普通函數(shù)的調(diào)用摄凡,此時的this被綁定到window

  • 最普通的函數(shù)調(diào)用
function fn1(){
      console.log(this)//window
    }
fn1()
  • 函數(shù)嵌套
function fn1(){
    function fn2(){
        console.log(this)//window
    }
    fn2()
}
fn1()
  • 把函數(shù)賦值之后再調(diào)用
var a = 1
var obj1 = {
    a:2,
    fn:function(){
        console.log(this.a)
    }
}
var fn1 = obj1.fn
fn1()//1

obj1.fn是一個函數(shù)function(){console.log(this.a)},此時fn1就是不帶任何修飾的函數(shù)調(diào)用蚓曼,function(){console.log(this.a)}.call(undefined)亲澡,按理說打印出來的 this 應(yīng)該就是 undefined 了吧,但是瀏覽器里有一條規(guī)則:

如果你傳的 context 就 null 或者 undefined纫版,那么 window 對象就是默認(rèn)的 context(嚴(yán)格模式下默認(rèn) context 是 undefined)

因此上面的this綁定的就是window床绪,它也被稱為隱性綁定。
如果你希望打印出2,可以修改fn1()為fn1.call(obj1)癞己,顯示地綁定this為obj1

  • 回調(diào)函數(shù)
var a = 1
function f1(fn){
    fn()
    console.log(a)//1
}
f1(f2)

function f2(){
    var a = 2
}

改寫代碼如下:

var a = 1
function f1(){
    (function (){var a = 2})()
    console.log(a)//1
}
f1()

(f2中的a只有f2內(nèi)部能訪問膀斋,f1只能延作用域鏈找,f2中把var去掉 則打印結(jié)果為2痹雅;)
仍舊是最普通的函數(shù)調(diào)用仰担,f1.call(undefined),this指向window绩社,打印出的是全局的a摔蓝。
借此,我們終于可以解釋為什么setTimeout總是丟失this了愉耙,因?yàn)樗簿褪且粋€回調(diào)函數(shù)而已贮尉。

setTimeout(function() {
    console.log(this)//window
    function fn(){
        console.log(this)//window
    }
    fn()
}, 0);
構(gòu)造器調(diào)用模式:

new一個函數(shù)時,背地里會將創(chuàng)建一個連接到prototype成員的新對象朴沿,同時this會被綁定到那個新對象上

function Person(name,age){
// 這里的this都指向?qū)嵗?    this.name = name
    this.age = age
    this.sayAge = function(){
        console.log(this.age)
    }
}

var dot = new Person('Dot',2)
dot.sayAge()//2

this 指向構(gòu)造函數(shù)實(shí)例

call

call 方法第一個參數(shù)是要綁定給this的值(指定的對象猜谚,該對象就是函數(shù)執(zhí)行的上下文),后面?zhèn)魅氲氖且粋€參數(shù)列表赌渣。當(dāng)?shù)谝粋€參數(shù)為null魏铅、undefined的時候,默認(rèn)指向window锡垄。

var arr = [1, 2, 3, 89, 46]
var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4])//89
var obj = {
    message: 'My name is: '
}

function getName(firstName, lastName) {
    console.log(this.message + firstName + ' ' + lastName)
}

getName.call(obj, 'Dot', 'Dolby')
apply

apply接受兩個參數(shù)沦零,第一個參數(shù)是要綁定給this的值,第二個參數(shù)是一個參數(shù)數(shù)組货岭。當(dāng)?shù)谝粋€參數(shù)為null路操、undefined的時候,默認(rèn)指向window千贯。

var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89

事實(shí)上apply 和 call 的用法幾乎相同, 唯一的差別在于:當(dāng)函數(shù)需要傳遞多個變量時, apply 可以接受一個數(shù)組作為參數(shù)輸入, call 則是接受一系列的單獨(dú)變量屯仗。

ar obj = {
    message: 'My name is: '
}

function getName(firstName, lastName) {
    console.log(this.message + firstName + ' ' + lastName)
}

getName.apply(obj, ['Dot', 'Dolby'])// My name is: Dot Dolby

bind

和call很相似,第一個參數(shù)是this的指向搔谴,從第二個參數(shù)開始是接收的參數(shù)列表魁袜。區(qū)別在于bind方法返回值是函數(shù)以及bind接收的參數(shù)列表的使用。

  • bind返回值是函數(shù)
var obj = {
    name: 'Dot'
}

function printName() {
    console.log(this.name)
}

var dot = printName.bind(obj)
console.log(dot) // function () { … }
dot()  // Dot

bind 方法不會立即執(zhí)行敦第,而是返回一個改變了上下文 this 后的函數(shù)峰弹。而原函數(shù) printName 中的 this 并沒有被改變,依舊指向全局對象 window芜果。

  • 參數(shù)的使用
function fn(a, b, c) {
    console.log(a, b, c);
}
var fn1 = fn.bind(null, 'Dot');

fn('A', 'B', 'C');            // A B C
fn1('A', 'B', 'C');           // Dot A B
fn1('B', 'C');                // Dot B C
fn.call(null, 'Dot');      // Dot undefined undefined

call 是把第二個及以后的參數(shù)作為 fn 方法的實(shí)參傳進(jìn)去鞠呈,而 fn1 方法的實(shí)參實(shí)則是在 bind 中參數(shù)的基礎(chǔ)上再往后排。

有時候我們也用bind方法實(shí)現(xiàn)函數(shù)珂里化右钾,以下是一個簡單的示例:

var add = function(x) {
  return function(y) {
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

在低版本瀏覽器沒有 bind 方法蚁吝,我們也可以自己實(shí)現(xiàn)一個旱爆。

if (!Function.prototype.bind) {
    Function.prototype.bind = function () {
        var self = this,                        // 保存原函數(shù)
            context = [].shift.call(arguments), // 保存需要綁定的this上下文
            args = [].slice.call(arguments);    // 剩余的參數(shù)轉(zhuǎn)為數(shù)組
        return function () {                    // 返回一個新函數(shù)
            return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
        }
    }
}

function () {
  var fn=this;
  var context=arguments[0];
  var args=Array.prototype.slice.call(arguments,1);
  return function () {
    return fn.apply(context,args)
  }
}

應(yīng)用場景

  • 求數(shù)組中的最大和最小值
var arr = [1,2,3,89,46]
var max = Math.max.apply(null,arr)//89
var min = Math.min.apply(null,arr)//1

  • 將類數(shù)組轉(zhuǎn)化為數(shù)組
var trueArr = Array.prototype.slice.call(arrayLike)

  • 數(shù)組追加
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var total = [].push.apply(arr1, arr2);//6
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]

  • 判斷變量類型
function isArray(obj){
    return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('dot') // false

  • 利用call和apply做繼承
function Person(name,age){
    // 這里的this都指向?qū)嵗?    this.name = name
    this.age = age
    this.sayAge = function(){
        console.log(this.age)
    }
}
function Female(){
    Person.apply(this,arguments)//將父元素所有方法在這里執(zhí)行一遍就繼承了
}
var dot = new Female('Dot',2)

  • 使用 log 代理 console.log
function log(){
  console.log.apply(console, arguments);
}
// 當(dāng)然也有更方便的 var log = console.log()

總結(jié)

call、apply和bind函數(shù)存在的區(qū)別:

bind返回對應(yīng)函數(shù), 便于稍后調(diào)用窘茁; apply, call則是立即調(diào)用怀伦。

除此外, 在 ES6 的箭頭函數(shù)下, call 和 apply 將失效, 對于箭頭函數(shù)來說:

  • 箭頭函數(shù)體內(nèi)的 this 對象, 就是定義時所在的對象, 而不是使用時所在的對象;所以不需要類似于var _this = this這種丑陋的寫法
  • 箭頭函數(shù)不可以當(dāng)作構(gòu)造函數(shù),也就是說不可以使用 new 命令, 否則會拋出一個錯誤
  • 箭頭函數(shù)不可以使用 arguments 對象,山林,該對象在函數(shù)體內(nèi)不存在. 如果要用, 可以用 Rest 參數(shù)代替
  • 不可以使用 yield 命令, 因此箭頭函數(shù)不能用作 Generator 函數(shù)房待,什么是Generator函數(shù)可自行查閱資料,推薦閱讀阮一峰Generator 函數(shù)的含義與用法捌朴,Generator 函數(shù)的異步應(yīng)用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吴攒,一起剝皮案震驚了整個濱河市张抄,隨后出現(xiàn)的幾起案子砂蔽,更是在濱河造成了極大的恐慌,老刑警劉巖署惯,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件左驾,死亡現(xiàn)場離奇詭異,居然都是意外死亡极谊,警方通過查閱死者的電腦和手機(jī)诡右,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轻猖,“玉大人帆吻,你說我怎么就攤上這事×撸” “怎么了猜煮?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長败许。 經(jīng)常有香客問我王带,道長,這世上最難降的妖魔是什么市殷? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任愕撰,我火速辦了婚禮,結(jié)果婚禮上醋寝,老公的妹妹穿的比我還像新娘搞挣。我一直安慰自己,他們只是感情好音羞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布囱桨。 她就那樣靜靜地躺著,像睡著了一般黄选。 火紅的嫁衣襯著肌膚如雪蝇摸。 梳的紋絲不亂的頭發(fā)上婶肩,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音貌夕,去河邊找鬼律歼。 笑死,一個胖子當(dāng)著我的面吹牛啡专,可吹牛的內(nèi)容都是我干的险毁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼们童,長吁一口氣:“原來是場噩夢啊……” “哼畔况!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慧库,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤跷跪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后齐板,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吵瞻,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年甘磨,在試婚紗的時候發(fā)現(xiàn)自己被綠了橡羞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡济舆,死狀恐怖卿泽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滋觉,我是刑警寧澤签夭,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站椎瘟,受9級特大地震影響覆致,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肺蔚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一煌妈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宣羊,春花似錦璧诵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苛坚,卻和暖如春比被,著一層夾襖步出監(jiān)牢的瞬間色难,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工等缀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枷莉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓尺迂,卻偏偏與公主長得像笤妙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子噪裕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345