在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,都應該掌握其用法。上面有許多不對的地方還請大家指正,歡迎批評!!