關於call(),apply(),bind()函數的理解

在Javascript中,我們比較難以理解的東西就是this,它表示在當前作用域中的上下文。有時候我們需要在使用過程中改變this的指向,那麼就需要通過這三種方法:call(),apply(),bind()。

具體什麼意思呢?說的太官方可能還是不懂,大致意思就是,有兩個對象,其中一個對象想要使用另一個對象中的方法,這個時候就要用到這個三個方法。

舉個例子:有兩個人張三和李四,張三會說話並說的很好,李四是個啞巴,說不出來,這時候李四特別的想說話,憋得滿臉通紅說不出來,這時候他知道張三會說話,所以李四就把張三拉過來,用手比劃給張三,讓張三替他說話。

說了這麼一大堆,還是想說一下概念,call(),apply(),bind()我爲了改變某個函數的上下文來使用的。

函數其實就是對象,當每個函數在創建時,我們都知道,在函數內部會自動創建arguments和this兩個對象,用來代表函數所接收的參數和一個運行環境的上下文this.
但與此同時,函數上還自帶幾個屬性和方法如:
這裏寫圖片描述
從圖中我們可以看到,比較熟悉的如arguments,name.length,hasOwnProperty等屬性都在,而且還有apply,call,bind這三個,他們都是方法,都是自帶的,所以使用起來也是沒問題的。

下面就舉例子來看看這三個函數是怎麼用的。

    var person1 = {
        name:'張三',
        say:function(city){
            console.log(this.name + '說你好,非常歡迎您來到' + city + '!');
        }
    };

    var person2 = {
        name:'李四'
    };

上面列出了兩個人,張三和李四,張三有個say()的功能,李四卻沒有。

    person1.say('北京');//張三說你好,非常歡迎您來到北京!
    person2.say('北京');//person2.say is not a function

可以看到,person2.say()提示了錯誤,但是我們現在想說話,而且又不想加新的屬性該怎麼辦呢?這個時候我們的call,apply,bind就可以派上用場了。

    person1.say.call(person2,'北京');//李四說你好,非常歡迎您來到北京!
    person1.say.apply(person2,['北京']);//李四說你好,非常歡迎您來到北京!
    person1.say.bind(person2)('北京');//李四說你好,非常歡迎您來到北京!

可以看到,通過上面三句代碼,都可以讓我們的李四開口說話,並使用張三的方法。
這三個函數第一個參數都代表原函數的上下文應該更改成這個對象。
很顯然從上面代碼可以看出來三個方法的用法是不同的。
1 call()和apply()方法都是直接對方法的調用,而bind()執行過後返回一個函數。
2 call()方法從第二個參數開始依次向函數中傳參,而apply()方法第二個參數是一個參數數組,不過他也是按照在數組中的順序依次向函數中傳參的。
3 bind()函數倒是沒有規範傳參的方式,在bind()函數中第二個參數也行,這裏放一個數組也行,在外面調用的時候傳參同樣可以,不過要注意的是bind()函數返回的是一個函數,需要調用這個函數纔會執行,而且,bind()方法只能使用一次,連續多個bind()函數是不管用的

    var foo = {
        foo:2,
        sum:function(x,y){
            return this.foo + x + y;
        }
    };

    var bar = {
        foo:5
    };

     var aa = {
        foo:12
    };
    console.log(foo.sum(5,6));//13
    console.log(foo.sum.call(bar,5,6));//16
    console.log(foo.sum.apply(bar,[5,6]));//16
    console.log(foo.sum.bind(bar)(5,6));//16
    console.log(foo.sum.bind(bar,5,6)());//16
    console.log(foo.sum.bind(bar,5)(6));//16
    console.log(foo.sum.bind(bar,5).bind(aa)(6));//16

從上面可以看到,bind()函數的傳參方式可以在bind函數體內,也可以在調用函數括號內,但是不管怎麼樣,他都是從bind()函數第二個參數開始算起的,bind()函數的傳參方式和call()方法是一樣的,不能傳參數數組。最後一句話我們可以看到,在bind()了一個bar對象之後,又進行了一次bind(),這次bind()到了aa,但是打印出來的結果仍然是16,看來,bind()函數只能使用一次,多的是不管用的。

如果第一個參數是null,那麼上下文就會指向全局,將會是在全局作用域中使用這個函數。

    var color = 'white';
    var obj = {
        color:'yellow',
        say:function(){
            console.log('The color is ' + this.color);
        }
    };

    var green = {
        color:'green'
    };

    obj.say();//The color is yellow
    obj.say.call(green);//The color is green
    obj.say.call(null);//The color is white

第三句代碼中call()方法傳入了null,因此他將指向全局作用域,我們在全局作用域中也定義了color,所以他自然打印出了white.

通過這些特性,我們衍生出了很多巧妙的用法:
1 類的繼承:

     function Parent(name,age){

        this.name = name;
        this.age = age;

    }

    function Child(name,age,height){
        Parent.call(this,name,age);
        this.height = height;
    }

2 數組的合併:

    var arr1 = [1,33,5,3,23];
    var arr2 = [55,43,12,56];
    var newArr = Array.prototype.push.apply(arr1,arr2);

    console.log(arr1);//[1, 33, 5, 3, 23, 55, 43, 12, 56]

我們知道push()方法貌似一次只能想數組中添加一個元素,如果使用apply()方法,apply的參數是一個數組,因此使用apply()之後一次就可以添加整個數組了。

3 數組中的大小值

    var arr1 = [1,33,5,3,23];

    var max = Math.max.apply(Math,arr1);
    var min = Math.min.apply(Math,arr1);
    console.log(max);//33
    console.log(min);//1

Math.max(arg1,arg2,arg3,…),使用apply之後就可以將數組參數放入其中,就可以計算出我們想要的值。

總之:call(),apply()和bind()方法就是用來改變函數體內上下文的,這三個方法用的非常的多,作爲一名FE,都應該掌握其用法。上面有許多不對的地方還請大家指正,歡迎批評!!

發佈了32 篇原創文章 · 獲贊 6 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章