js中this的全部可能性

前言

this指針真是前端初學者比較頭疼的一塊內容了,面試筆試中也基本是必出內容,可是不真正搞懂的話就很難答出,可是我發現先去理解再去作題反倒效率很是低下,每次都是作了下題就把上題遺忘了。可是我發現每次遇到的面試題目卻能記憶深入。
我決定反其道而行之,先找出全部this的可能性作一遍,再反過來反思當時的思路哪裏有問題, 很快的我就基本掌握了全部this的題目,我在下面理出我所知道的全部this類型,若是有遺忘再回來查找回想。
如有遺漏或錯誤歡迎各位大佬在評論區補充。html

萬能三句

首先,請看三句話,帶着這三句話去思考全部題目。
1.this永遠指向對象
2.this取決於js運行時的調用者,而不是上下文中的位置
3.經過函數名()直接調用:this指向window。(嚴格模式下爲undefined)
前端

普通對象

對象中使用this應該是最多見的了,先上例子。面試

var fullName = "三角";
var obj = {
    fullName: "方塊",
    prop: {
        fullName: "圓",
        getFullName: function () {
            return this.fullName;
        }
    }
}
console.log(obj.prop.getFullName()); 

var getFullName = obj.prop.getFullName;  
console.log(getFullName());  
複製代碼

答案是:圓 三角
這一題的第一問關鍵就是,套嵌的對象中,內部的this指向內部對象。因此輸出了圓。
segmentfault

第二問,咱們須要先把代碼變形一下,方便理解。
當執行到 var getFullName = obj.prop.getFullName; 此時的代碼已經變成windows

var getFullName = function () {
    return this.fullName;
}
console.log(getFullName());  //undefined
複製代碼

時咱們須要想到上面的三句話中的2和3, 便可理解。(不說的太透徹,第二問是筆試常考內容,須要本身好好領悟)數組

new出來的對象

當使用new(經過構造函數)生成一個對象,這個對象下的全部this都會默認綁定在這個對象上。 做爲對象方法調用時,this指向調用該方法的對象。bash

function Func() {
            this.fullName = '方塊';
            this.getFullName = function () {
                return this.fullName;
            }
        }
        let func = new Func();
        console.log(func.getFullName());
複製代碼

答案是方塊閉包

普通函數

借用上面的例子。app

var getFullName = function () {
    return this.fullName;
}
console.log(getFullName());  
複製代碼

答案是undefined
首先,想到三句話中的3,便可知道在window下直接調用的都指向window。聯繫到第一句話:this永遠指向對象---由於它已在最外層,因此指向的對象就是window對象。函數

函數做爲數組的一個元素,經過數組下標調用

function func() {
    console.log(this);
}

var arr = [func, 1, 2, 3];
arr[0]();  
複製代碼

答案是 this--->arr
首先要知道的是:js中array是特殊的對象, 這裏不作拓展。聯想到三句話中的1,2。

系統內置函數的回調函數

function func() {
    console.log(this);
}

setTimeout(func, 1000);//
setInterval(func,1000);//
複製代碼

答案是 this--->window this--->window
能夠那麼理解:系統內部函數的調用者就是windows對象。

閉包

那麼你們思考一下,上面這個問題能夠解決嗎?

那就是經過閉包, 若沒有什麼想法,來看答案。

var obj = {
    name: 'test',
    foo: function () {
        console.log(this);
    },
    foo2: function () {
        var that = this;
        setTimeout(function () {
            console.log(this);  // Window
            console.log(that);  // Object -->obj
        }, 1000);
    }
}
obj.foo2();
複製代碼

成功獲取到了外部的this。 衆所周知,閉包的特性就是:函數內部能夠引用外部的參數和變量。 。 經過一個變量來保存指針,再經過閉包來調用到它的外部屬性

匿名函數

(function(){console.log(this === window)})();  //true
複製代碼

匿名函數的this指向window
詳細解答請前往 www.zhihu.com/question/21…

當即執行函數

先看題。

var bo = 10;
function foo() {
  console.log(bo);
}
(function() {
  var bo = 20;
  foo();
})();
(function (func) {
  var bo = 30;
  func();
})(foo)
複製代碼

答案是10,10
我是那麼理解的:首先咱們須要知道的是,當即執行函數會造成一個塊級做用域。 因此兩次var給bo賦值都並無影響到window下的bo,這很是關鍵。
第二點,咱們看到萬能三句中的1,3都適用這題。 由於當即執行函數不是對象,因此塊級做用域的this指向了它的上級window。

箭頭函數

箭頭函數沒有this!它的this是從它的調用者那裏借來的。因此即便對其適用bind、call、apply也沒有效果
事先申明:mozilla推薦在通常狀況下不要在對象中使用箭頭函數
我建議:用到this的方法都不要用箭頭函數,出了問題真的很難發現。
可是咱們仍是來舉個例子。

var x = 11;
        var obj = {
            x: 22,
            say: () => {
                console.log(this.x);
            }
        }
        obj.say(); 
複製代碼

答案是11
這裏容易錯的問題就是this爲何沒有指向obj卻指向了window,這裏就要引出一個執行上下文的概念。say這個箭頭函數所在的父執行環境不是obj,而是window。
要注意:簡單對象(不是經過構造函數生成的)是沒有執行上下文的!

再來一個例子加深印象

function Func() {
            this.fullName = '方塊';
            this.props = {
                fullName: '圓',
                getFullName: ()=>{
                    return this.fullName;
                },
                getFullName1: function(){
                    return this.fullName;
                }
            }
        }

        let func = new Func();
        console.log(func.props.getFullName()); 
        console.log(func.props.getFullName1()); 
複製代碼

答案是:方塊 圓
getFullName方法的執行環境不是props(由於他沒有),而是func中,因此this指向func對象,返回方塊。

call、apply、bind方法

你們都知道,這三個方法的功能就是:強制改變this指向
有兩點要注意的是:

  • 參數指定爲null或undefined時,this指向window。
  • 指定爲原始值(數字,字符串,布爾值),this會指向該原始值的自動包裝對象
var fullName = "三角";
        function Func() {
            this.fullName = '方塊';
            this.getFullName = function () {
                return this.fullName;
            }
        }
        function Func1() {
            this.fullName = '方塊1';
            this.getFullName = function () {
                return this.fullName;
            }
        }
        let func = new Func();
        let func1 = new Func1();
        console.log(func.getFullName());//方塊
        console.log(func.getFullName.call(null)) //三角
        console.log(func.getFullName.call(func1)) //方塊1
複製代碼

答案是:方塊 三角 方塊1
本來指向func的方法,經過call讓它從新指向了window或func1,apply和bind同理。

最後

其實這些包括萬能三句都仍是取巧的方法,要想真正從根本理解this,還須要從執行環境 詞法做用域 做用域鏈 的相關知識。同時也能夠經過this的這些問題來鞏固基礎知識,豈不美哉!

參考資料:
皇問天 www.cnblogs.com/huangwentia…
知乎 www.zhihu.com/question/21…
技術雜談 zhuanlan.zhihu.com/p/24107744
當即執行函數的做用域鏈是什麼樣子 segmentfault.com/q/101000000…
Sugar www.cnblogs.com/var-chu/p/8…
安歌 segmentfault.com/a/119000001…

相關文章
相關標籤/搜索