1、call&apply |
call, apply都屬於Function.prototype的方法,由於屬於Function.prototype,因此每一個Function對象實例,也就是每一個方法都有call, apply屬性啦。html
若是不明白,請見「Javascript之一切皆爲對象3」。chrome
並且它們的做用都是同樣的,只是使用方式不一樣而已。數組
做用:借用別人的方法來調用,就像本身有這個方法同樣。瀏覽器
咦,那它們怎樣才能達到這目的呢?app
對象。函數
對象?ui
是的,其實就是改變執行上下文對象的內部指針,由於在Javascript中,代碼總有一個執行上下文對象,那麼當我手動改變它時,就能夠改變這個執行上下文啦,也就能夠利用非本身的方法成爲本身的方法哦。this
咱們一塊兒來寫個Demo。spa
假如,我有一個方法a,它的做用是輸出對象的名字this.name;那麼當我使用call或者apply改變它的執行上下文對象時,它的輸出結果是不同的。prototype
什麼意思?
詳情請見下代碼:
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'windowName'; //方法a的做用,是輸出對象的名字 function a(){ console.log(this.name); } function b(){ this.name = 'bName'; } //將a方法的執行上下文對象指向window a.call(window); //將a方法的執行上下文對象指向new b() a.call(new b()); </script> </body> </html>
執行上述代碼,結果以下:
看見了麼?因此說call,apply的做用就是借用別人的方法,改變別人方法的執行上下文對象爲本身,成爲本身的方法,爲己所用。
注意: call或apply的第一個參數傳的是什麼,它們就會將其默認爲執行上下文對象。假若咱們沒有指明call或apply的執行上下文對象,即,call和apply的第一個參數是null、undefined或爲空時,在非嚴格模式下,函數內的this指向window或global,瀏覽器就是window。嚴格模式下,null爲null,undefined或空爲undefined。
什麼意思,請見下面的demo(僅以call舉例且爲非嚴格模式):
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function print(){ console.log(this); }; //將第一個參數數字1,做爲執行上下文對象 print.call(0,1,2); //將第一個參數字符串'123',做爲執行上下文對象 print.call('123'); //將第一個參數true,做爲第執行上下文對象 print.call(true); //將第一個參數對象,做爲執行上下文對象 print.call(new Object()); //將null傳入 print.call(null); //將undefined傳入 print.call(undefined); //不傳任何參數 print.call(); </script> </body> </html>
看見了麼,我上面傳入的依次是數字,字符串,true,對象,null,undefined和空,獲得下面的結果:
那麼,call與apply既然做用同樣,那它們有什麼區別呢?
它們的第一個參數,毋庸置疑,都是傳入的執行上下文對象,區別是從第二個參數開始的。call方法的其它參數依次傳遞給借用的方法做參數,而apply就兩個參數,第二個參數爲一個數組傳遞。
簡單點,就是:
fun.call(obj, arg1, arg2…) === fun.apply(obj, [arg1, arg2…]) === obj.fun(arg1, arg2…);
咦,call和apply的區別是,參數的傳遞不一樣,有什麼用呢?
根據它們傳遞參數的區別,當參數明確的時候,使用call;當傳遞的參數不明確時,用 apply咯,即傳遞arguments給apply做爲第二個參數。
好了,光說不作沒用,咱們寫個demo看看。
<!DOCTYPE html> <head> <title>call&apply</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function print(name, age, time){ console.log("name: "+ name +" age: "+ age +" time: "+ time ); }; function fn(a, b, c){ //使用call,參數明確 print.call(this,a); //使用apply,參數明確 print.apply(this,[a, b]); //使用apply,參數不明確 print.apply(this,arguments); } fn('monkey',24,'1992'); </script> </body> </html>
執行上述代碼,結果以下:
call與apply,這下明白了麼?
2、bind |
bind,最開始認識它的時候,理解就是改變執行上下文的對象。
好比,當咱們使用setTimeout時,默認匿名函數裏的this指向的是window,但使用對象的方法時,我想將this指向對象呢,怎麼辦呢?其中的一個方法就是使用bind。
(關於setTimeout的理解,見「setTimeout那些事兒」)。
如:
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'window'; var obj = { name:'monkey', print: function(){ //在這裏使用bind,顯示地將this指向obj,因此console.log會輸出'monkey' setTimeout(function(){ console.log(this.name); }.bind(this),100); } }; obj.print(); </script> </body> </html>
執行上述代碼結果爲:
好了,既然談到bind是改變執行上下文中的對象,我靠,那咱們怎麼不使用call或apply呢?
call或apply不也是改變執行上下文的對象麼?
是的,咱們將上面的demo修改下,將bind換成call,代碼以下:
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> var name = 'window'; var obj = { name:'monkey', print: function(){ setTimeout(function(){ console.log(this.name); }.call(this)/*在這裏將bind換成call*/,100); } }; obj.print(); </script> </body> </html>
打開chrome調試器,得下結果:
咦,我靠,這不是和bind同樣麼?
是的,但若是咱們將setTimeout的延遲時間,換成2秒,或者更長呢?打開chrome調試器,運行修改後的代碼,你就會發現區別,call或apply是立馬呈現’monkey’,而bind是在延遲相應時間後,呈現’monkey’。
Why?
由於call或apply是將執行上下文對象換了後,當即執行;而bind是將執行上下文對象換了後,建立一個新函數。
咱們再一塊兒寫個demo看看。
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function fun(){ console.log(this.name); } function obj1(){ this.name = 'call||apply'; } function obj2(){ this.name = 'bind'; } var o1 = new obj1(); var o2 = new obj2(); fun.call(o1); fun.bind(o2); </script> </body> </html>
執行上述代碼,結果爲:
咦,怎麼只打印了一個’call||apply’呢?
由於咱們在上面的代碼中,bind我只是綁定了對象o2,可是它又不當即執行,而是返回一個新函數哦。
咱們修改以上代碼,手動執行bind返回後的新函數看看。
<!DOCTYPE html> <head> <title>bind</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <script> function fun(){ console.log(this.name); } function obj1(){ this.name = 'call||apply'; } function obj2(){ this.name = 'bind'; } var o1 = new obj1(); var o2 = new obj2(); fun.call(o1); //手動調用bind建立的新函數 fun.bind(o2)(); </script> </body> </html>
運行代碼:
嘿嘿,這下對了吧。
因此,必定要記住bind方法會建立一個新函數,稱爲綁定函數,當調用這個綁定函數時,綁定函數會以建立它時傳入的第一個參數做爲this,即執行上下文對象。
好了,晚安everyone~