爲何須要這些?主要是由於this,來看看this乾的好事。數組
box.onclick = function(){
function fn(){
alert(this);
}
fn();
};瀏覽器
咱們本來覺得這裏面的this指向的是box,然而倒是Window。通常咱們這樣解決:app
box.onclick = function(){
var _this = this;
function fn(){
alert(_this);
}
fn();
};函數
將this保存下來。this
還有一些狀況,有時咱們想讓僞數組也可以調用數組的一些方法,這時call、apply、bind就派上用場了。prototype
咱們先來解決第一個問題修復this指向。對象
box.onclick = function(){
function fn(){
alert(this);
}
fn();
};ip
改爲以下:字符串
box.onclick = function(){
function fn(){
console.log(this);
}
fn.call(this);
};io
很神奇吧,call的做用就是改變this的指向的,第一個傳的是一個對象,就是你要借用的那個對象。
fn.call(this);
這裏的意思是讓this去調用fn這個函數,這裏的this是box,這個沒有意見吧?若是這個你不清楚,極可能你是javscript的新朋友。box調用fn,這句話很是重要,咱們知道this它始終指向一個對象,恰好box就是一個對象。那麼fn裏面的this就是box。
box.onclick = function(){
function fn(){
console.log(this);
}
fn.call(this);
};
這句話在某些狀況下是能夠簡寫的,好比:
box.onclick = function(){
var fn = function(){
console.log(this); //box
}.call(this);
};
或者這樣:
box.onclick = function(){
(function(){
console.log(this);
}.call(this)); //box
};
又或者這樣:
var objName = {name:'JS2016'};
var obj = {
name:'0 _ 0',
sayHello:function(){
console.log(this.name);
}.bind(objName)
};
obj.sayHello();//JS2016
call和apply、bind可是用來改變this的指向的,但也有一些小小的差異。下面咱們來看看它們的差異在哪。
function fn(a,b,c,d){
console.log(a,b,c,d);
}//call
fn.call(null,1,2,3);//apply
fn.apply(null,[1,2,3]);//bind
var f = fn.bind(null,1,2,3);
f(4);
結果以下:
1 2 3 undefined
1 2 3 undefined
1 2 3 4
前面說過第一個參數傳的是一個你要借用的對象,但這麼咱們不須要,全部就傳了一個null,固然你也能夠傳其餘的,反正在這裏沒有用到,除了第一個參數後面的參數將做爲實際參數傳入到函數中。
call就是挨個傳值,apply傳一個數組,bind也是挨個傳值,但和call和apply還有多少不一樣,使用call和apply會直接執行這個函數,而bind並不會而是將綁定好的this從新返回一個新函數,何時調用由你本身決定。
var objName = {name:'JS2016'};
var obj = {
name:'0 _ 0',
sayHello:function(){
console.log(this.name);
}.bind(objName)
};
obj.sayHello();//JS2016
這裏也就是爲何我要用bind的緣由,若是用call的話就會報錯了。本身想一想這個sayHello在obj都已經執行完了,就根本沒有sayHello這個函數了。
這幾個方法使用的好的話能夠幫你解決很多問題好比:
正常狀況下Math.max只能這樣用
Math.max(10,6)
但若是你想傳一個數組的話你能夠用apply
var arr = [1,2,30,4,5];
console.log(Math.max.apply(null,arr));
又或者你想讓僞數組調用數組的方法
function fn(){
[].push.call(arguments,3);
console.log(arguments); //[1, 2, 3]
}
fn(1,2);
再者:
var arr = ['aaabc'];
console.log(''.indexOf.call(arr,'b')); //3
牛逼不,簡直偷樑換柱,固然還有不少能夠利用的,本身盡情花輝去吧。
簡單說一下這種偷樑換柱的原理吧,實際上瀏覽器內部根本就不在意你是誰,它只關心你傳給個人是否是我可以運行的,以下:
正常狀況
var str = 'aaabc';
console.log(str.indexOf('b'));
而這種狀況其實作的事情和上面如出一轍,看我來拆解。
var arr = ['aaabc'];
''.indexOf.call(arr);
這句話就是說讓arr調用字符串的indexOf方法,前面說過了瀏覽器內部不在意你是誰,因此誰均可以來調用,但不是100%成功,具體看以下。
''.indexOf.call(arr,'b')
這裏的arr就是['aaabc'],內部極可能拆成了'aaabc',所以就成了下面的這段代碼。
'aaabc'.indexOf('b');
這就是它們的祕密。
這裏得說一下bind在某些瀏覽器下不兼容。咱們來模擬一個玩玩。
Function.prototype.$bind = function(obj){
//保存當前this
var _this = this;
//截取除了第一個之外的全部實際參數
var a = [].slice.call(arguments,1);
//返回一個新函數
return function(){
//讓當前那個調用的函數的this指向obj,而且把實參傳給它,這裏用了concat是由於,咱們可能在綁定之後還傳遞參數,因此才把他們合併起來。如f(4)這個是在綁定之後傳的參數,a這個argument是綁定時的。
_this.apply(obj,a.concat([].slice.call(arguments)));
};
};function fn(a,b,c,d){
console.log(a,b,c,d);
}var f = fn.$bind(null,1,2,3);
f(4);
這個方法和實際上的bind仍是差異很大的,如
var arr = ['JSS'];var index = ''.indexOf.$bind(arr,'S');
console.log(index())------------------
function fff(){
[].push.$bind(arguments,1);
console.log(arguments);
}fff();
這些都無法使用,由於技術有限就沒辦法帶你們封裝一個完美的bind了,若是有須要網上搜索一下吧。