博客 / 詳情

返回

call、apply、bind函數詳解

 我們都知道call,apply,bind函數都是為了改變this的指向,那麼對於三種函數有什麼相同點有什麼不太點或者有什麼應用呢?下面我們來進行介紹

call與apply函數

 在javascript種,call,apply的出現是為了改變函數體內部this的指向,下面我們來看一個栗子,並從中進行分析。

      var a = "我是window的小a";
       var obj = {
           a:"我是obj的小a",
           foo:function (...arg) {
               console.log(this.a,this,...arg)
           }
       }

       obj.foo()    //此時的this為obj

       var f2 = obj.foo
       f2()     //此時的this為window

       f2.call(obj,1,2,3)  //call改變了this的指向,此時的this為obj,並傳入參數    
       f2.call(obj,[1,2,3]) //輸出obj中的a
       f2.apply(obj,[1,2,3],3,4,5) //apply改變了this的指向為obj,傳入參數數組,在參數數組之後傳遞參數,並不能傳入該參數
       f2.apply(obj,1,2,3)  //報錯!apply第二個參數必須為參數數組

我們來看一下輸出結果。
在這裏插入圖片描述
 由上述輸出結果來看,使用call和apply能夠改變this的指向。函數f2原本的指向為window,使用call和apply函數綁定obj後this的指向為obj。對於上述的輸出結果還有一個報錯?立馬不淡定了。原來apply的第二個參數只能傳入參數數組,不能傳入多個參數。

對於上述輸出結果的小總結為:

  • call和apply都用於去改變this的指向問題,第一個參數為this所指向的對象
  • call和apply都為直接調用函數,返回值就為調用函數的返回值。
  • call的第二個位置和之後傳遞參數列表,當向call中傳遞數組時,則視為只傳遞了一個參數(這個參數為數組)
  • apply的第二個位置只能傳遞參數數組,在參數數組之後傳遞參數,均失效。

根據call和apply的特點,我們可以有以下應用。

使用call進行繼承

 function father(name,age,hometown,hobby) {
            this.name = name;
            this.age = age;
            this.hometown = "中國"
            this.hobby = hobby;
        }
        function son(name,age,hobby,hometown) {
            father.call(this,name,age,hometown)//繼承父類中的多個屬性
            this.hobby = hobby;
        }
        
        let f = new father("小頭爸爸",38,"中國","釣魚");
        let s = new son("大頭兒子",16,"打球");
        
        console.log(f)
        console.log(s)

輸出結果:
在這裏插入圖片描述

使用call判斷數據類型

        console.log(Object.prototype.toString.call("pp"))
        console.log(Object.prototype.toString.call({age:15}))
        console.log(Object.prototype.toString.call(23))
        console.log(Object.prototype.toString.call([1,2,3]))

在這裏插入圖片描述
 在這我就不一一列舉關於數據的數據類型了,此時肯定有人產生疑惑,為什麼平時我們用的obj.toString和object.prototype.toString.call(obj)的結果為什麼不一樣了呢?因為我們使用的obj.toString()在一些數據類型中都重寫了toString的方法,對於函數和數組來講,使用obj.toString都會直接輸出字符串,故使用obj.toString()不能輸出數據類型。但為什麼Object.prototype.toString.call(obj)能夠輸出數據類型的值呢?因為toString為Object的原型方法,並沒有像obj.toString()一樣重寫該方法。使用Object上的原型中的toString方法的返回值為數據類型。故我們可以通過Object.prototype.toString.call(obj)來判斷數據類型。

使用call使偽數組使用數組方法

     <div class="xixi"></div>
    <div class="xixi"></div>
    <div class="xixi"></div>
    <div class="xixi"></div>
    <script>
        //將偽數組轉換為數組,使其可以調用數組所具有的方法
        var realArr = Array.prototype.slice.call(document.getElementsByTagName("div"));
        realArr.push(1)
        console.log(realArr)
        //偽數組
        var div = document.getElementsByTagName("div")
        div.push(1)  //報錯為偽數組中沒有數組具有的方法
    </script>

輸入結果:
在這裏插入圖片描述

 對於我們調用的一些方法例如document.getElementsByName()、document.getElementsByTagName() ,childNodes/children 等返回的均為偽數組,此時不能使用數組的方法,我們可以使用call函數使其轉換為真正數組所帶有真正數組方法的對象,這個時候我們就能夠調用數組的所有方法啦。

使用apply進行數組的連接

        var arr1 = [1,"xixi",{age:17},34]
        var arr2 = [3,"haha",{age:44},21]
        Array.prototype.push.apply(arr1,arr2)//進行數組的連接
        console.log(arr1)

在這裏插入圖片描述

使用apply獲取數組中的最大值

        var arr1 = [1,3,4,5,93]
        //實際為向Math.max中傳入參數arr1,等同於Math.max(arr1)
        var max = Math.max.apply(Math.max,arr1)
        console.log(max)

bind函數

 與call和apply相似,其作用都是用於改變函數內部this的指向。但第二個參數開始是傳入參數列表,這點與call相似,但與其有什麼不同的地方呢?call與apply是立即執行函數而bind函數是將函數返回。

綁定函數

使用bind方法會返回一個函數,使其函數無論怎麼調用,其this都會指向原來所綁定的那個對象

       var a = "我是window的小a";
       var obj = {
           a:"我是obj的小a",
           foo:function (...arg) {
               console.log(this.a,this,...arg)
           }
       }

       obj.foo()    //此時的this為obj

       var f1 = obj.foo()  //此時的this為window

       var f2 = f1.bind(obj)  //綁定this為obj,並返回一個新函數
       f2() //輸出我是obj的小a

配合使用setTimeout

在我們沒有給setTimeout綁定this的情況下,當我們在setTimeout中使用this,this關鍵字會指向window對象,我們可以利用bind函數綁定this,使其能夠利用this調用想要綁定的函數。

    function foo() {
        this.age = 20
    }
    //函數f1
    foo.prototype.f1 = function () {
        console.log(this)   //此時的this為foo
        setTimeout(this.f2,1000) //但是此時的this為window,而不是foo,因此無法調用f2
        var _this = this   //獲取到當前的this
        setTimeout(this.f2.bind(_this),1000) //將this.f2的this綁定給foo
    }
    //函數f2
    foo.prototype.f2 = function () {
        console.log("我的年齡為:" + this.age)
    }
    var foo = new foo()  //創造實例
    foo.f1()

使用bind將類數組轉換為數組

function foo() {

        var TempSlice = Array.prototype.slice;
        // 把函數的call方法綁定在數組slice方法上,之後再給call方法傳遞參數
        var slice = Function.prototype.call.bind(TempSlice);
        return slice(arguments);

    }
    console.log(foo(1,2,3))   //[1,2,3]

 

區別bind()、call()、apply()

  • 都能指定函數中的this
  • call()和apply是立即調用函數
  • bind()是將函數返回

 
 
對於bind(),cal(),apply()的介紹就到這裏啦,有幫助的話就點個讚唄,歡迎評論區指正!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.