在JS中,this指向是一個難點,在本文中講解幾種常見的this指向問題,並介紹一下call,apply,bind這三個函數的用法。javascript
首先要明白一點就是,函數裏面纔會有this,而this指向是由函數執行的方式來決定的,而且this指向的必定是對象,常見的有三種狀況:java
下面咱們來看幾個例子:數組
案例1:app
var oBox = document.getElementById('box'); function a() { console.log( this ); } a(); document.onclick = a; //oBox.onclick = a;
打開控制檯,會看到打印了一個window,這是第6行a(),a函數自執行時,函數內部的this指向window對象。而後,咱們點擊一下頁面,觸發了document的onclick事件,此時控制檯會再打印一個document,只是由於事件函數內部的this指向觸發該事件的對象。同理,若是咱們把第7行註釋,第8行取消註釋,代碼功能換成給頁面上一個id爲‘box’的標籤對象註冊一個點擊事件,那麼當咱們在頁面上點擊這個標籤的時候,控制檯會給咱們打印出這個標籤(以下圖)。函數
案例二:this
var obj = { name:'Person', fn:function(){ console.log(this); } } obj.fn();
這個案例裏,fn是對象obj的一個方法,當咱們用obj.fn()執行的時候,fn裏的this指向obj,咱們會看到控制檯打印的效果以下:spa
案例三:對象
var a = function () { console.log( this ); }; function x() { a(); } x();
這個案例裏的this指向的是window,即咱們開篇說的第一種狀況。這裏可能有一個誤導的地方就是,有人會以爲a在x中運行,this會指向x,其實咱們只要再認真讀一下咱們以前說過的兩點就會明白了,一點是this指向必定是一個對象,這裏x不是對象,另外一點就是函數名加括號自執行,this指向window,無論這個函數在放在哪裏執行的,哪怕是放在一個對象裏面,只要它是函數名加括號自執行的方式,那麼它裏面的this指向必定是window。blog
案例四:事件
function x() { function a() { console.log(this); } a(); } x();
這裏的this也是指向window,吃透了案例三,再看案例四就以爲簡單了。
案例五:
var oBox = document.getElementById("box"); oBox.a = function () { console.log(this); }; oBox.a(); (oBox.a)(); document.onclick = oBox.a;
這個案例裏,第7行和第8行打印的this爲id是box的標籤,當咱們點擊一下頁面後,會打印document,由於不管什麼形式的事件函數,裏面的this都會指向觸發該事件的對象。如下是控制檯的打印結果:
this剖析:this自己就是一個對象,跟其餘數據類型同樣,JS也會在內存中給它分配一個地址,只是是動態的,不是固定的,它的變化是根據不一樣執行環境來動態分配的。而this是何時產生呢,就是this指向的對象是何時產生,this就是何時產生,咱們前面總結的第一種狀況,函數名加括號自執行,其實是window.函數名()執行的,因此this會指向window,那麼實際上和第二種狀況對象.函數名()執行方式是同樣的。
在咱們的實際運用中,有時候咱們須要改變this的指向,JS給咱們提供了三個改變this執行的方法:call,apply和bind,讓咱們先來看看它們的用法和不一樣之處。
在函數執行階段使用,能夠改變this指向;
call的第一個參數表明函數的this指向;
原函數的第一個形參對應call的第二位實參,第二個形參對應call的第三位實參,以此類推。
在函數執行階段使用,能夠改變this指向;
apply的第一個參數表明函數的this指向;
apply的第二個參數是個數組,數組第一位對應原函數第一個形參,以此類推。
並不會馬上幫助函數去自執行,當函數執行的時候去改變this指向;
參數形式和call同樣。
它們的用法是:函數名.call(arg1,arg2,agr3......)、函數名.apply(arg1,[arg2,arg3......])、函數.bind(arg1,arg2,arg3......),主要的區別在於call和apply會讓函數當即自執行,而bind不會讓函數自執行;而call和apply的區別在於傳參的不一樣,apply的第二個參數是一個數組。下面咱們來看幾個實際的例子:
案例六:
function a(x,y) { console.log(x+y); console.log(this); } a(1,2); a.call(document,5,5); a.apply(oBox,[5,1]);
這個案例裏,第6行a函數自執行了一次,第7行用call來執行a函數,改變this指向爲document,而第8行用apply來執行函數a,改變其this指向爲oBox對象,而且這裏傳的第二個參數用了一個數組。最終的打印結果以下:
案例七:
function a(x) { console.log(x); console.log(this); } a.call(null,1); a.apply(undefined,[2]);
當咱們給call、apply和bind的第一個參數傳null或者undefined時,this指向window,有時候咱們只須要利用call、apply、bind傳參而不須要改變this指向時,咱們會這樣用。下面咱們一塊兒來看打印的結果:
案例八:
var oBox = document.getElementById("box"); oBox.x = function () { console.log(this); }; oBox.x(); oBox.x.call(document);
對象的方法依然能夠經過call、apply和bind改變this指向。效果以下:
下面咱們來看看bind,它的用法和call是相似的,不一樣在於它不會當即執行函數,而是在函數被動執行的時候再去改變this指向,咱們看下面的例子:
案例九:
var oBox = document.getElementById("box"); var oWrap = document.getElementById("wrap"); oBox.onclick = eFn.bind(oBox,200,150); oWrap.onclick = eFn.bind(oWrap,150,200); function eFn(x,y) { this.style.width = x+"px"; this.style.height = y+"px"; }
頁面上有兩個div標籤,一個id爲box,一個id爲wrap,咱們但願實現一個功能,就是點擊它們的時候,它們分別按照不一樣的值去改變寬高,由於功能一致,因此咱們把它們的點擊事件函數進行封裝,在封裝函數中改變當前點擊對象的寬高,由於點擊事件是被動觸發的,因此此時咱們會用到bind,經過bind巧妙傳遞參數而且改變this指向,達到咱們想要實現的功能,這個案例是bind的一個經典運用。
值得一提的是,apply和bind除了改變this指向的功能,在傳參方式上也給咱們提供了一些好的解決辦法,bind能夠在傳參的同時不主動執行這個函數,給咱們在寫事件函數傳參和定時器函數傳參時提供了方便,而apply用數組傳參的方式也給咱們提供了新的便利,好比下面的例子:
案例10:
咱們須要找出一個數字數組中的最大值,咱們知道Math有個max方法是找最大值的,可是max接收的參數是分開的一個個的數字,而不是數組,因此找出數組的最大值,一般咱們的作法是這樣的:
var arr = [3,16,5,6,9,12,32,21]; var max =arr[0]; for (var i = 1; i < arr.length; i++) { if(arr[i]>max) max = arr[i]; } console.log(max);
可是apply給咱們提供了一個更好的解決方式,以下:
var arr = [3,16,5,6,9,12,32,21]; var max = Math.max.apply(null,arr); console.log(max);
以上是本文的所有內容,感謝閱讀!