前端基礎進階(三):全方位解讀this

結論:
一 :在一個函數上下文中,this由調用者提供,由調用函數的方式來決定。若是調用者函數,被某一個對象所擁有,那麼該函數在調用時,內部的this指向該對象。若是函數獨立調用,那麼該函數內部的this,則指向undefined。可是在非嚴格模式中,當this指向undefined時,它會被自動指向全局對象。面試

從結論中咱們能夠看出,想要準確肯定this指向,找到函數的調用者以及區分他是不是獨立調用就變得十分關鍵。segmentfault

// demo01
var a = 20;
function fn() {
    console.log(this.a);
}
fn();  //fn()是調用者,爲獨立調用,非嚴格模式下指向所有對象window,所以結果爲20
// demo02
var a = 20;
function fn() {
    function foo() {
        console.log(this.a);
    }
    foo();
}
fn(); //fn()是調用者,爲獨立調用,非嚴格模式下指向所有對象window,所以結果爲20
// demo03
var a = 20;
var obj = {
    a: 10,
    c: this.a + 20,
    fn: function () {
        return this.a;
    }
}

console.log(obj.c);  // 對象obj中的c屬性使用`this.a + 20`來計算。這裏咱們須要明確的一點是,單獨的`{}`是不會造成新的做用域的,所以這裏的`this.a`,因爲並無做用域的限制,因此它仍然處於全局做用域之中。因此這裏的this實際上是指向的window對象,所以結果爲40。
console.log(obj.fn());  //fn()是調用者,他不是獨立調用,被對象obj所調用,所以它的this指向指向obj所以結果爲10

再來看一些容易理解錯誤的例子,加深一下對調用者與是否獨立運行的理解。app

var a = 20;
var foo = {
    a: 10,
    getA: function () {
        return this.a;
    }
}
console.log(foo.getA()); // 10

var test = foo.getA;
console.log(test());  // 20

foo.getA()中,getA是調用者,他不是獨立調用,被對象foo所擁有,所以它的this指向了foo。而test()做爲調用者,儘管他與foo.getA的引用相同,可是它是獨立調用的,所以this指向undefined,在非嚴格模式,自動轉向全局window。函數

稍微修改一下代碼,你們自行理解。this

var a = 20;
function getA() {
    return this.a;
}
var foo = {
    a: 10,
    getA: getA
}
console.log(foo.getA());  // 10

靈機一動,再來一個。以下例子。code

function foo() {
    console.log(this.a)
}

function active(fn) {
    fn(); // 真實調用者,爲獨立調用
}

var a = 20;
var obj = {
    a: 10,~~~~
    getA: foo
}

active(obj.getA); // 20

二:使用call,apply顯示指定this

JavaScript內部提供了一種機制,讓咱們能夠自行手動設置this的指向。它們就是call與apply。全部的函數都具備着兩個方法。它們除了參數略有不一樣,其功能徹底同樣。它們的第一個參數都爲this將要指向的對象。對象

以下例子所示。fn並不是屬於對象obj的方法,可是經過call,咱們將fn內部的this綁定爲obj,所以就可使用this.a訪問obj的a屬性了。這就是call/apply的用法。繼承

function fn() {
    console.log(this.a);
}
var obj = {
    a: 20
}
fn.call(obj); //結果爲20

三:箭頭函數 this指向

箭頭函數中,沒有this。若是你在箭頭函數中使用了this,那麼該this必定就是外層的this。也正是由於箭頭函數中沒有this,所以咱們也就無從談起用call/apply/bind來改變this指向。
在ES6中,會默認採用嚴格模式,所以this也不會自動指向window對象了,而箭頭函數自己並無this,所以this就只能是undefined,這一點,在使用的時候,必定要慎重慎重再慎重,否則踩了坑你都不知道本身錯在哪!這種狀況,若是你還想用this,就不要用使用箭頭函數的寫法。ip

箭頭函數 this指向是在定義的時候處在的對象就是它的this。箭頭函數的this看外層的是否有函數,若是有,外層函數的this就是內部箭頭函數的this,若是沒有,則this是window。作用域

var a = 10;
var obj = {
    a:99,
    fn1: () => {
    console.log(this.a)},
    fn2:function () {
    console.log(this.a)}
    }
    obj.fn1(); //10
    obj.fn2(); //99
let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                btn1.onclick = () => {
                    console.log(this);//obj
                };
            }
        };
        obj.getName();

上例中,因爲箭頭函數不會建立本身的this,它只會從本身的做用域鏈的上一層繼承this。

那假如上一層並不存在函數,this指向又是誰?

let btn2 = document.getElementById('btn2');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: () => {
                btn2.onclick = () => {
                    console.log(this);//window
                };
            }
        };
        obj.getName();

上例中,雖然存在兩個箭頭函數,其實this取決於最外層的箭頭函數,因爲obj是個對象而非函數,因此this指向爲Window對象
參考文章:
全方位解讀this
你還沒搞懂this

典型面試例子及答案:

// 修改$對象裏面的代碼,使得如下代碼運行正常
var $ = {
fn:function () {
console.log(1);
},
fn2:function () {
console.log(2);
}
}
$.fn().fn2();
// 答案:
var $ = {
fn:function () {
console.log(1);
return this; // 返回this,能夠繼續調用函數
        },
fn2:function () {
console.log(2);
        }
    }

$.fn().fn2();
相關文章
相關標籤/搜索