博客 / 詳情

返回

面試 | 你不得不懂的 JS this 指向

推薦閲讀地址

掘金

前言:大家好,我是林一一,這是一篇關於 this 指向的文章。this 是 JS 中一個特別重要的一個知識點,this 難嗎?好像挺簡單的。看完下面這一篇文章,還不會的話,你別來找我。手動狗頭🐕。

思維導圖

thissiwei.png

this 的指向

  • 執行函數前有 '.' 點操作符的話,函數體中的 this 就指向前面的對象,沒有就指向 window,嚴格模式下指向 undefined。這句話特別的重要,請記住
  • 函數沒有直接調用者 this 指向全局對象(瀏覽器中是window,node中是 global)。如匿名函數等
  • 構造函數的 this 指向實例本身。
  • 箭頭函數本身沒有this的,箭頭函數的 this 指向最近的非箭頭函數 this,找不到就指向 window,嚴格模式下指向 undefined
再來看一下這句話:執行函數前有 '.' 點操作符的話,函數體中的 this 就指向前面的對象,沒有就指向 window

一、普通函數 this 的熱身題

熱身題 1
var name = '林一一'
function fn(){
    var name = '林二二'
    return this.name
}
fn()    // 林一一
執行函數 fn(),前面沒有 '.' 點操作符吧,那麼這裏的 this 就指向 window。輸出的就是全局下的 name = '林一一'

再來看一下這句話:執行函數前有 '.' 點操作符的話,函數體中的 this 就指向前面的對象,沒有就指向 window

熱身題 2
var name = '林二二'
var obj = {
    name: '林一一',
    fn: function () {
        return this.name
    }
}
console.log(obj.fn())   // '林一一'
var fo = obj.fn
console.log(fo())       // '林二二'     fo() ==> window.fo()
obj.fn() 中函數 fn() 前面有 '.' 點操作符吧,那麼這裏的 this 就指向 obj 這個對象。再看執行函數 fo(),前面沒有 '.' 點操作符吧,那麼這裏的 this 就指向 window。其實上面的函數 fo() ==> window.fo(),所以執行函數 fo() 前面也是可以看作是有 '.' 操作符的。

再來看一下這句話:執行函數前有 '.' 點操作符的話,函數體中的 this 就指向前面的對象,沒有就指向 window

熱身題 3,修改一下熱身題 2
var name = '林二二'
var obj = {
    name: '林一一',
    fn: function () {
        var name = '小三'
        return function(){
             return this.name
        }
    }
}
console.log(obj.fn()())   // 林二二
var fo = obj.fn()
console.log(fo())    // 林二二
熱身3和熱身2差不多,obj.fn()()obj.fn()執行完後有一個函數(這裏稱為函數 A)返回,最後相當於執行函數 A()A() 前面沒有 '.' 點操作符吧,那麼這裏的 this 就指向 window,輸出就是 林二二 了。上面的 fo() 函數同理。

二、函數沒有直接調用者

函數沒有直接調用者 this 指向全局對象(瀏覽器中是window,node中是 global)。如匿名函數等

熱身題 1
var name = '林一一';
!(function(){
   console.log(this.name)   // 林一一
})()
自執行函數沒有直接的調用者輸出的 name = '林一一'
熱身題 2
var name = '林一一'
var obj = {
    name : '二二',
    callback: function(){
        console.log(this.name)
    }
}

setTimeout(obj.callback,1000)
/* 輸出
*   林一一
*/
函數 setTimeoutobj.callback(這只是一個引用地址) 中並沒有直接調用者,this 就指向 window。所以輸出的 name 就是全局下的 林一一

三、構造函數中的 this

來讀一下這句話:構造函數的 this 指向實例本身

關於構造函數的 this 為什麼指向實例是瀏覽器指定的,詳情看 new 這個過程發生了什麼 面試 | 你不得不懂得 JS 原型和原型鏈
熱身題 1
function Fn(){
    var n = 0
    this.name = '林一一'
    this.age = 18
    this.getName = function(){
        return this.name
    }
}

Fn.prototype.getAge = function(){
    return this.age
}

Fn.x = '林二二'

var f = new Fn()
console.log(f.name)     // 林一一
console.log(f.getName())     // 林一一
console.log(f.getAge())        // 18
console.log(f.n)    // undefined
console.log(f.x)    // undefined
上面的 Fn 經過 new後就是一個構造函數,this 就指向實例 f。所以上面的1,2輸出都是林一一f.getAge() 是實例 f 調用了getAge 輸出就是 18,問:實例 f 中並沒有屬性 getAge 是怎麼輸出 18的,f.x 輸出又為什麼是 undefined ?答:這是原型鏈的查找機制,屬性 x 不是在原型 prototype 上的就不是實例的屬性,可以讀一下這篇文章 面試 | 你不得不懂得 JS 原型和原型鏈;問:為什麼f.n 輸出的是 undefined。因為變量 n 是構造函數的私有變量和 new 創建的實例沒有關係。

四、箭頭函數

  • 箭頭函數本身沒有 this,箭頭函數的this繼承上下文的,裏面的 this會指向當前最近的非箭頭函數的 this,找不到就是 window (嚴格模式是undefined)
  • 箭頭函數的 this 始終指向函數定義時的 this,而非執行時
熱身題 1
var name = '林一一'
var obj = {
    name: '二二',
    a: () => {
        console.log(this.name)
    }
}
obj.a()

/* 輸出
*   '林一一'
*/
箭頭函數的 this,找不到非箭頭函數的 this 就直接指向 window
熱身題 2
var name = '林一一'
var obj = {
    name: '二二',
    fn: function() {
        return () => {
            console.log(this.name)
        }
    }
}
obj.fn()()

/* 輸出
*   '二二'
*/
很明顯箭頭函數的 this 來自函數 fn,對象 obj 調用了函數 fn,所以 fnthis 指向 obj,輸出結果就是 二二

五、call,apply,bind 改變 this 的指向

提示:所有的函數都是基於 Function 這個基類來創建的,同樣擁有 Function 原型上面的方法

  • call,接受this的對象,和一組列表。applycall 一樣,唯一不同的是 apply 接受的是一個包含多個參數的數組。bind 同樣也是改變函數的 this 指向,只不過 bind 執行後會返回一個新的函數,新函數中參數來源於剩餘的參數
熱身題
var name = '林一一'
var age = 18
function fn(){
   return this.name
}

function p(){
    return {
        age: this.age,
        arg: arguments
    }
}

let obj = {
    name: '二二',
    age: 18
}

let o = {
    name: '三三'
}

fn()    // '林一一'
fn.call(obj)    // '二二'
fn.call(o)  //  '三三'
p.call(obj, 12, 23, 45, 67) // {age: 18, arg: Arguments(4)}

fn.apply(obj)    // "二二"
p.apply(obj, [1, 2, 3, 4, 5])    // {age: 18, arg: Arguments(5)}

fn.bind(obj)()  // "二二"
p.bind(obj, 12, 23, 34)()   // {age: 18, arg: Arguments(3)}
以上就是 call, apply, bind, 關於 this 的內容,這裏不介紹三者的寫法,如果介紹可以寫另一篇文章了。對這三者不熟悉的可以找其他資料看看。

思考題

1. 筆試題 this 指向問題

var name = '林一一'
var obj = {
    name: '林二二',
    show: function (){
        console.log(this.name)
    },
    wait: function () {
        var fn = this.show
        fn()
    }
}
obj.wait()  //  林一一
obj.wait() 中,執行函數 wait() 前面有 '.' 點操作符吧,那麼這裏的 this 就指向 obj 這個對象,所以this.show ==> obj.show。再看執行函數 fn() 前面沒有 '.' 點操作符吧,那麼這裏的 this 就指向 window,輸出就是 林一一

2. 和閉包有關的 this 指向問題

var n = 2 // -> 4 -> 8
var obj = {
    n: 3,    // 6
    fn: (function(n){
        n*=2
        this.n+=2    // window 下的 n 變成 4
        var n = 5    // 這一步不會再重新聲明,因為已經參數賦值,就不會再聲明而是直接賦值 n = 5
        console.log("window.n", window.n)
        return function (m) {
            console.log("n:", n, "m", m)    // n:5  m:3  這裏的 n 向上查找是 5   // 
            this.n*=2    // fn(3): 2 * 4 =8  //  obj.fn(3): 2 * 3 = 6 
            console.log(m + (++n))    // 3 + (++5) ++n 導致上級作用域的n變成了6    // 3 + (++6)
        } 
    })(n)
}

var fn = obj.fn;
fn(3)    // 9
obj.fn(3)    // 10
console.log(n, obj.n)    // 8 6
/* 輸出
* 9
* 10
* 8  6
/
這道題就留給大家思考了,上面有我的分析步驟,覺得礙眼的話可以去掉 😂。你可以在評論區給出你的分析過程。

結束

感謝閲讀早這裏,我是林一一,如果這文章能對你有一點啓發的話,歡迎給個 star, 下次見。
user avatar 1023 頭像 laughingzhu 頭像 peter-wilson 頭像 tigerandflower 頭像 ivyzhang 頭像 sunhengzhe 頭像 zhangxishuo 頭像 columsys 頭像 jianqiangdepaobuxie 頭像 ailim 頭像 mulander 頭像 pugongyingxiangyanghua 頭像
12 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.