this總結【1】—— this概覽

1. this是什麼

this這個單詞是一個代詞,因此this應該是 指代某些東西
搞清楚this的關鍵之處,就是要搞清楚this指代了什麼node

那麼this到底指代了什麼呢?
就像你平時
指着一個蘋果說 this is an apple
指着一個香蕉說 this is a banana面試

一樣,this也會由於狀況的不一樣而不一樣segmentfault

在JavaScript中
按照常規理解,this的值是什麼取決於函數如何被調用
然而,this的值是什麼徹底取決於對應函數被call時傳入的第一個參數,而這通常是由js提早就預置好的數組

2. 函數如何被調用

2.1 常規理解

注意:這種方法能夠判斷通常狀況下的this是什麼
  1. 判斷this值就看函數是怎麼被調用的[函數有全局調用方法調用]
  2. this指向調用它的距離最近的那一個
// 例子
    function fn() {
        console.log(this)
    }

    let obj = {
        a: '',
        fn: fn
    }

    // 場景一
    fn() // window[瀏覽器] or global[node]

    // 場景二
    obj.fn() // obj

    // 場景一:全局調用
    // 場景二:方法調用

function VS method瀏覽器

  1. function xx(){...} 是function
  2. method某個對象下面的方法 如 obj = {fn:function(){...}} obj.fn() 叫作方法[method]
更詳細的說明請看 深刻淺出面向對象和原型【概念篇1】
// test1
    function fn() {
        console.log(this)
    }

    let obj1 = {
        wrapper: {
            a: 'a',
            fn: fn
        }
    }
    obj1.wrapper.fn() // ?
    // fn()是被當作wrapper的方法被調用的,因此this是wrapper
    // this指向調用它的距離最近的那一個

    // test2
    function executeCallback(cb) {
        cb()
    }

    executeCallback(obj1.wrapper.fn)
    // this是全局變量
    // 由於executeCallback函數是把cb當成全局函數進行調用的
    // 不像是obj.cb(),如果obj.cb()就是方法調用了
按照常規理解已經能夠解決大部分的面試、筆試題了,但若是想究this之根本,還須要經過call

2.2 用call理解

注意:經過對call的初步理解,你能夠知道this的值爲何是這樣的,但這種思惟不方便你猜this

首先,咱們要清楚this在函數中的位置app

function fn1([this], parameter) {
        // this就是隱藏的第一個參數,並且永遠是第一個參數
        console.log(this)
    }

其次,咱們要知道call的語法函數

fun.call(thisArg, arg1, arg2, ...)

OK,如今咱們要知道你在使用call的時候
call傳入的第一個參數能夠指定(改變)fn1的第一個參數,也就是隱藏的this測試

因此,爲何直接全局調用fn1時,打印出來的是window呢?
由於js已經幫你call了this

// 你寫的代碼
    fn1()

    // 在你寫上面代碼的時候,js幫你寫了一行你看不到的代碼
    fn1().call(undefined)

    // 你可能回問:這樣打出來的值應該是undefined呀
    // 但瀏覽器有個機制
    // 若是你傳的 thisArg 就 null 或者 undefined,那麼 window 對象就是默認的 context(嚴格模式下默認 context 是 undefined)
咱們從新來看待一下全局調用和方法調用,以及js是如何用call指定this的
// 例子
    function fn() {
        console.log(this)
    }

    let obj = {
        a: '',
        fn: fn
    }

    // 場景一
    fn() // window[瀏覽器] or global[node]
    // js幫你寫的代碼
    fn().call(undefined)

    // 場景二
    obj.fn() // obj
    // js幫你寫的代碼
    obj.fn().call(obj)
小測試(小陷阱)
let module = {
        x: 42,
        getX: function () {
            return this.x;
        }
    }

    let retrieveX = module.getX;
    console.log(retrieveX()); // undefined
    console.log(module.getX()) // 42

2.3 嚴格模式下的this

嚴格模式下只須要 注意一點就行,其它狀況下與非嚴格模式相同

全局做用域裏函數中的this是undefinedcode

function test() {
    "use strict"
    console.log(this) 
}
test() // undefined

因此,在使用構造函數時若是忘了加new,this再也不指向全局對象,而是報錯,由於這就是函數的全局調用

let People = function (name) {
    "use strict"
    this.name = name
}
People() // Cannot set property 'name' of undefined

3.幾種複雜狀況中的this

3.1 數組中的this

function fn() {
    console.log(this)
}

arr[fn, fn2, fn3]

arr[0]() // ??

// answer:arr

// 解析
// 數組也是對象的一種
// arr[0]() 能夠看作 arr.0().call(arr)

3.2 嵌套函數中的this

// 例子一
function fn0() {
    function fn() {
        console.log(this);
    }

    fn();
}

fn0(); // fn中this是全局變量

// 例子二
let a = {
    b: function () {
        console.log(this) // {b:fn}
        function xx() {
            console.log(this) // window
        }
        xx()
    }
}
a.b()

3.3 setTimeout、setInterval中的this

document.addEventListener('click', function (e) {
    console.log(this);
    setTimeout(function () {
        console.log(this); // 這裏的this是全局變量
    }, 200);
}, false);

3.4 事件中的this

document.querySelector('div').addEventListener('click',function (e) {
    console.log(this) // <div></div>
})
// 事件裏的this指向的是觸發事件的DOM節點

3.5 使用new時的this

請看深刻淺出面向對象和原型【番外篇——從新認識new】

4.總結

若是幾種複雜狀況下的this你看完了,你應該會有疑惑
爲啥我明白了全局調用和方法調用,但this的值仍然和我推斷的不同呢?

我說過了,this的值是call的第一個參數,js已經給不少this進行call了

因此,總結一下

  1. this的值是什麼,你別猜,極可能會和你想的不同
  2. console出來或者去看文檔、看源碼纔是最穩妥的選擇
  3. 若是你非要猜,就套用一下我這篇博客寫到的這幾種狀況
  4. 若是你怕猜錯,又要用到this,很簡單,直接用bind去強制指定this
關於call/apply/bind 請看 this總結【2】—— call/apply和bind

5.參考

this 的值究竟是什麼?一次說清楚

相關文章
相關標籤/搜索