this總結【2】—— call/apply和bind

1.call/apply和bind概覽

  1. 咱們要將call/apply歸爲一類,bind單獨歸爲一類
  2. 三者的共同點是均可以指定this
  3. call/apply和bind都是綁定在Function的原型上的,因此Function的實例均可以調用這三個方法
Function.prototype.call(this,arg1,arg2)
Function.prototype.apply(this,[arg1,arg2])
Function.prototype.bind(this,arg1,arg2)

至於爲何,看完這篇文章你就懂了:)javascript

若是你不懂什麼是實例的話,請移步 深刻淺出面向對象和原型【概念篇1】深刻淺出面向對象和原型【概念篇2】

2. call / apply —— 第一個參數是this(上下文)

2.1 做用和返回值

做用
  1. 調用函數
  2. 改變該函數this值
  3. 操做參數
返回值
返回值是你調用的方法的返回值,若該方法沒有返回值,則返回undefined。
window.a = 1

    function print(b, c) {
        console.log(this.a, b, c)
    }

    print(2, 3) // 1 2 3

    print.call({a: -1}, -2, -3) // -1 -2 -3
    print.apply({a: 0}, [-2, -3]) // 0 -2 -3

call()方法的做用和 apply() 方法是同樣的,只有一個區別
call()方法接受的是若干個參數
apply()方法接受的是一個包含若干個參數的數組java

2.2 apply傳遞數組的應用

// 例子一
    // Math.max()不接收數組的傳遞,咱們可使用apply方法
    let answer = Math.max.apply(null, [2, 4, 3])
    console.log(answer) // 4

    // 注意下面三個等價
    Math.max.apply(null, [2, 4, 3])
    Math.max.call(null, 2, 4, 3)
    Math.max(2, 4, 3)
// 例子二
    // 合併兩個數組
    let arr1 = ['parsnip', 'potato']
    let arr2 = ['celery', 'beetroot']
    // 將第二個數組融合進第一個數組
    // 至關於 arr1.push('celery', 'beetroot');
    Array.prototype.push.apply(arr1, arr2)
    // 注意!!!this的意思是誰調用了push這個方法
    // 因此當 this = arr1 後
    // 就成了 arr1 調用了 push方法
    // 因此上述表達式等價於 arr1.push('celery', 'beetroot') 

    console.log(arr1)
    // ['parsnip', 'potato', 'celery', 'beetroot']

例子二中很是值得注意的就是arr2數組被拆開了,成了一個一個的參數segmentfault

因此,apply常常性的做用之一就是 將數組元素迭代爲函數參數
// 例子三
    Math.max.apply(null, [2, 4, 3]) // 完美運行
    arr1.push.apply(null, arr2) // 報錯 Uncaught TypeError: Array.prototype.push called on null or undefined

    // 說明
    Math = {
        max: function (values) {
            // 沒用到this值
        }
    }
    Array.prototype.push = function (items) {
        // this -> 調用push方法的數組自己
        // this爲null的話,就是向null裏push
        // Array.prototype.push called on null or undefined
    }
    // 下面三個值是徹底等價的,由於this值已是arr1
    Array.prototype.push.apply(arr1, arr2)
    arr1.push.apply(arr1, arr2)
    arr2.push.apply(arr1, arr2)

2.3 小測試

function xx() {
        console.log(this)
    }
    xx.call('1') // ??
    xx() // ??
若是答案和你想的不同,請移步 this總結【1】—— this概覽

3.bind

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

3.1做用

  1. 改變this
  2. 返回一個新函數

3.2 綁定函數、目標函數

實例使用bind()方法後會返回一個新的函數【綁定函數】
原函數爲【目標函數】數組

我我的更喜歡用 新函數原函數來區分,由於新名詞越多,理解上的困難越大

那麼新函數被調用時會發生什麼呢?
下面一句話務必記住
其實就是把原函數call/apply一下,並指定你傳遞的thisapp

function xx() {
        console.log(this)
    }

    let foo = xx.bind({'name':'jason'})
    // foo —— 新函數【綁定函數】
    // xx —— 原函數【目標函數】

    foo()

    // 新函數調用時對原函數的操做
    
    // 下面是僞代碼
    // function foo(){
    //     xx.call({'name':'jason'})
    // }

    // 1.給xx(原函數)指定this 2.調用xx(原函數)
    // 必定要注意這兩步是在新函數被調用時才發生,不調用不發生
    // 你也能夠總結爲一句話,給原函數 call/apply 了一下

3.3 bind()傳參和新函數【綁定函數】傳參

  1. bind(this,arg1)會將arg1插入到原函數【目標函數】的參數列表的開始位置
  2. 傳遞給新函數【綁定函數】的參數會跟在它們的後面
function list() {
        // 原函數【目標函數】
        return Array.prototype.slice.call(arguments);
    }

    let listTest = list(1, 2, 3); // [1, 2, 3]

    // 新函數【綁定函數】
    let leadingThirtysevenList = list.bind(undefined, 37);

    let list1 = leadingThirtysevenList(); // [37]
    let list2 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

3.3 原生實現一個bind,使用 this + call/apply【重點】

思考過程
// 實現bind其實就是實現bind的特色
    // 1.bind的第一個參數是this
    // 2.bind能夠return一個新函數,這個新函數能夠調用原函數而且能夠指定其this,還能夠接受參數
    // 3.bind返回的新函數傳遞的參數要在bind傳遞的參數的後面
代碼
Function.prototype._bind = function () {
        // 聲明bind接受的參數【除去this】爲bindArgs
        // 由於第一個參數是this,須要去掉
        let bindArgs = Array.prototype.slice.call(arguments, 1)
        let bindThis = arguments[1]
        // 聲明原函數【目標函數】爲targetObj
        let targetObj = this
        return function () {
            // return出來的的函數接受的參數爲newArgs
            // 要在return出來的新函數裏把bindArgs和newArgs合併,使用數組的concat方法
            let newArgs = Array.prototype.slice.call(arguments)
            return targetObj.apply(bindThis, bindArgs.concat(newArgs))
        }
    }

4. 既然都是指定this,爲何已經有call/apply,又要有bind呢?

4.1 你從未關注過函數的返回值

你在控制檯輸入console.log(1)爲何一個是1,一個是undefined?

clipboard.png

1是執行console.log()方法的輸出值,undefined是這個方法的返回值
因此,你要知道全部的函數都有返回值,必定要去關注一下函數的返回值

4.2 call/apply 與 bind 的返回值

xxx.call()/xxx.apply() 的返回值是由xxx自己的返回值決定的
xxx.bind() 的返回值是一個函數
相關文章
相關標籤/搜索