javascript中this 的指向問題(二)

接上一篇,(一)

三:顯式綁定
如果想要在某個對象上強制調用函數,通過call,apply方法,第一個參數是一個對象,是給this準備的,接着在調用函數時將其綁定到this。因爲可以直接指定this的綁定對象,所以稱爲顯式綁定。
上代碼:

function foo() {
        console.log(this.a);
    }
    var obj={
        a:2
    };
    foo.call(obj);

通過foo.call(obj);在調用foo時強制把他的this綁定到obj上。
1、如果你傳入了一個原始值(字符串類型、布爾類型或者數字類型)來當作this的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String(。。)等),這被稱爲“裝箱”。
顯示綁定仍然無法解決丟失綁定的問題。因此就出現了下面的幾種情況:
一一:硬綁定:顯式的強制綁定
上代碼:

function foo() {
        console.log(this.a);
    }
    var obj1={
        a:1
    };
    var obj2={
        a:2
    };
    var bar=function () {
        foo.call(obj1);
    };
    bar();//1。。。foo綁定到obj1
    setTimeout(bar,100);//1。。。這個會是最後一個輸出,因爲使用了setTimeout
    bar.call(obj2);//1。。。想要通過bar再改變this的指向已經不能改變了
    foo.call(obj2);//2。。。直接通過foo綁定obj2還是可以綁定的,因爲foo函數又不是obj1一個人的
    foo.bind(obj1).call(obj2);//1。。。bind也是綁定對象的一種方法,綁定之後也無法改變,而且這個會輸出1下面的一行代碼還會輸出一個錯誤的原因是bind綁定完obj1執行完foo後還會返回一個函數,用於後面的call方法綁定,但是this的指向已經不能改變了,但是還可以通過call方法繼續傳參數。
    foo.call(obj1).call(obj2);//先輸出一個1,再輸出TypeError: Cannot read property 'call' of undefined。。。。。。而這個foo第一個call方法執行完並不會返回一個函數,所以執行到後面的那個call的時候系統就會報錯
代碼解讀:創建了函數bar,並在他的內部手動調用了foo.call(obj1);因此強制把foo的this綁定到了obj1上。無論之後如何調用函數bar,它都會手動在obj1上調用foo。這種綁定就是一種顯式的強制綁定,稱爲硬綁定。

硬綁定的應用場景:創建一個包裹函數,負責接收參數並返回值:
上代碼:

    function foo(something) {
        console.log(this.a);//2
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar=function () {
        return foo.apply(obj,arguments);
    };
    var b=bar(3);
    console.log(b);//5

另一種使用方法是創建一個可以重複使用的輔助函數:
上代碼:

function foo(something) {
        console.log(this.a,something);//2,3
        return this.a+something;
    }
//    簡單的輔助綁定函數
    function bind(fn, obj) {
        return function () {
            return fn.apply(obj,arguments);
        }
    }
    var obj={
        a:2
    };
    var bar=bind(foo,obj);
    var b=bar(3);
    console.log(b);//5

由於硬綁定是一種非常常用的模式,ES5提供了內置的方法Function.prototype.bind

function foo(something) {
        console.log(this.a,something);//2,3
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar=foo.bind(obj);
    var b=bar(3);
    console.log(b);//5

bind(…)會返回一個硬編碼的新函數,它會把你指定的參數設置爲this的上下文並調用原始函數。硬綁定第一個例子中用到的bind就是這個。

二二:API調用的上下文

function foo(el) {
        console.log(el,this.id);//2,3
    }
    var obj={
        id:"idh"
    };
//    調用foo時把this綁定到obj
    [1,2,3].forEach(foo,obj);//1 idh 2 idh 3 idh

forEach函數:
array.forEach(callback [,thisobj]);下面鏈接給了詳細的forEach用法:
https://msdn.microsoft.com/library/ff679980(v=vs.94).aspx
這些函數實際上都是通過call或者apply實現了顯示綁定,這樣寫可以少寫一寫代碼而已。

四:new綁定

function foo(a) {
        this.a=a;
    }
    var bar=new foo(2);
    console.log(bar.a);//2

使用new來調用foo時,會構造一個新對象並把它綁定到foo調用中的this上,new是最後一種可以影響函數調用時this綁定行爲的方法,稱爲new綁定。
從理論上來講:
首先來看,使用new來調用函數的時候發生了什麼!
1、創建一個全新的對象;
2、這個新對象會被執行prototype連接;
3、這個新對象會綁定到函數調用的 this;
4、如果函數沒有返回其他對象,那麼new表達式中的函數調用會自動返回這個新對象。

以上。。。四種this的指向問題已經介紹完了, 但是實際現實中總是會碰到更復雜的有關this的問題。
如果某個調用位置可以應用多條規則,,就要考慮這四種規則的優先級問題了!下面一個話題,優先級!

優先級

其實就是通過代碼的運行結果來判斷,各種規則的優先級。
毫無疑問,默認綁定的優先級是四種規則中最低的。

1、顯示綁定>隱式綁定:

function foo(a) {
        console.log(this.a);
    }
    var obj1={
        a:2,
        foo:foo
    };
    var obj2={
        a:3,
        foo:foo
    };
//    隱式綁定
    obj1.foo();//2
    obj2.foo();//3
//    顯式綁定
    obj1.foo.call(obj2);//3
    obj2.foo.call(obj1);//2

2、new綁定>隱式綁定

function foo(something) {
        this.a=something;
    }
    var obj1={
        foo:foo
    };
    var obj2={};

//    隱式綁定
    obj1.foo(2);
    console.log(obj1.a);//2

//    顯式綁定
    obj1.foo.call(obj2,3);
    console.log(obj2.a);//3

//    new綁定
    var bar =new obj1.foo(4);
    console.log(obj1.a);//2
    console.log(bar.a);//4

3、new綁定>顯式綁定

function foo(something) {
        this.a=something;
    }
    var obj1={};

//    顯式綁定
    var bar=foo.bind(obj1);
    bar(2);
    console.log(obj1.a);//2

//    new綁定
    var baz =new bar(3);
    console.log(obj1.a);//2
    console.log(baz.a);//3

bar被硬綁定到obj1上,但是new bar(3)並沒有改變obj1.a=3的值。但是new還是修改了bar中的this。具體爲啥。。有關bind的實現。更復雜,我還沒有搞懂,23333zzz

在new中使用硬綁定函數的主要目的:預先設置函數的一些參數,這樣在使用new進行初始化時就可以只傳入其餘的參數。(有關“部分應用”,“柯里化”)
例如:`function foo(p1,p2) {
    this.val=p1+p2;
}
//  之所以只用null是因爲在本例中我們並不關心硬綁定的this是什麼
//    反正使用new時this會被修改
var bar=foo.bind(null,"p1");
var baz =new bar("p2");
console.log(baz.val);//p1p2`

所以,,,綜上所述:new綁定>顯式綁定>隱式綁定>默認綁定
判斷this的方法:
1、函數是否在new中調用(new綁定)?如果是的話this綁定的是新創建的對象

var bar=new foo();

2、函數是否通過call、apply(顯示綁定)或者硬綁定調用?如果是的話,this綁定的是指定的對象。

var bar =foo.call(obj2);

3、函數是否在某個上下文對象中調用(隱式綁定)?如果是的話,this綁定的是那個上下文對象。

var bar=obj1.foo();

4、如果都不是的話,使用默認綁定。如果在嚴格模式下就綁定到undefined,否則綁定到全局對象。

var bar=foo();

理解了一、二this綁定就基本明白了,不過。。凡事總有例外。下次再接着總結綁定例外和ES6中的this的用法,,,啊,這個this確實蠻複雜的,我也看了好久,,,還是這種系統的講容易理解,再加上接觸的多了慢慢就明白了。。。orz
應該還會有一個javascript中this 的指向問題(三)= =….

發佈了23 篇原創文章 · 獲贊 7 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章