this,call,apply,bind淺析

  在JS中,this指向是一個難點,在本文中講解幾種常見的this指向問題,並介紹一下call,apply,bind這三個函數的用法。javascript

1、常見的this指向狀況

  首先要明白一點就是,函數裏面纔會有this,而this指向是由函數執行的方式來決定的,而且this指向的必定是對象,常見的有三種狀況:java

  1. 函數名(),即函數名加括號自執行,該函數裏面的this指向window對象;
  2. 對象.方法(),即對象點屬性方法加括號執行,此方法內的this指向該對象;
  3. 不管什麼形式的事件函數,函數內部的this指向觸發事件的對象。

  下面咱們來看幾個例子:數組

案例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,那麼實際上和第二種狀況對象.函數名()執行方式是同樣的。

2、改變this指向

  在咱們的實際運用中,有時候咱們須要改變this的指向,JS給咱們提供了三個改變this執行的方法:call,apply和bind,讓咱們先來看看它們的用法和不一樣之處。

  1. call:

    在函數執行階段使用,能夠改變this指向;
    call的第一個參數表明函數的this指向;
    原函數的第一個形參對應call的第二位實參,第二個形參對應call的第三位實參,以此類推。

  2. apply:

    在函數執行階段使用,能夠改變this指向;
    apply的第一個參數表明函數的this指向;
    apply的第二個參數是個數組,數組第一位對應原函數第一個形參,以此類推。

  3. bind:(不支持IE8及如下)

    並不會馬上幫助函數去自執行,當函數執行的時候去改變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);

  以上是本文的所有內容,感謝閱讀!

相關文章
相關標籤/搜索