一次搞定 JavaScript 中的 this

本文首發於個人 GitHub 博客前端

小白前端學習 JavaScript 過程當中遇到的第一道坎,大機率就是 this 了。曾經我也被 this 搞的很是頭大,還看過很多博客,但我以爲大多數文章寫的有些複雜或者又引入了一些新的概念,加大了理解難度。本文就站在各位前輩大佬的肩膀上,作一下總結昇華,目的就是讓你們一次搞定 JavaScript 中的 this。git

本文會引入的概念

  1. 函數調用,知道 fnfn() 不同就行,fn() 是函數調用
  2. 隱式調用,不含有 callapplybind 的函數調用就是隱式調用,反之就是顯示調用
  3. 箭頭函數,知道長的像 fn = () => {} 這樣的函數叫箭頭函數就行
  4. call 的用法,知道 call() 的第一個參數能夠指定 fn 的 this 就行,bindapply 同理
  5. 形參、實參,知道函數定義時的參數叫形參,調用時傳入的參數叫實參就行

三步肯定 this

1. 一「定」——函數調用this定

函數調用的時候肯定 this 指向,能夠把 this 當作一個隱藏參數。github

var a = 10;
function fn() {
	var a = 1;
  	console.log(this.a);
}

fn() // 調用的時候才肯定this,默認是undefined,瀏覽器把它改爲了window,var聲明的變量會自動掛到window下,因此輸出10
複製代碼

2. 二「轉」——箭頭轉外,隱式轉call

箭頭函數調用

看這個函數的類型,若是是箭頭函數,看它外面的 this。面試

var fn = () => {
  console.log(this.id);
}
var id = 21;
foo(); // 21,由於外面的this是undefined,非嚴格模式下瀏覽器會自動轉成window,var聲明的變量被自動掛到window,因此輸出21
複製代碼

普通函數隱式調用

  1. fn(1, 2) 轉化成 fn.call(undefined, 1, 2)瀏覽器

  2. obj.fn('hello') 轉化成 obj.fn.call(obj, 'hello')markdown

  3. arr[0]() 轉化成 arr.0.call(arr, 'x')app

來一道綜合題一次性說完:函數

var id = 10
var obj = {
  id: 1,
  foo: function(){
    console.log(this.id)
  }
}
function fn (){ console.log(this) }
var arr = [fn, fn2]

var bar = obj.foo
obj.foo() // 1
bar() // 10
arr[0]() // arr
複製代碼

3. 三「特例」——三種特例別踩坑

new 會改變 this

function Person (name) {
  this.name = name
}
var name = 'window'
var person1 = new Person('Varian')
console.log(person1.name) // Varian
複製代碼

嚴格模式保持 undefined

嚴格模式下值爲 undefined 的 this 不會變成 windowoop

"use strict"; // 開啓嚴格模式
var a = 10;
function foo () {
  console.log('this1', this) // 'this1' undefined
  console.log(window.a) // 10
  console.log(this.a) // Uncaught TypeError: Cannot read property 'a' of undefined
}
console.log(window.foo) // f foo() {...}
console.log('this2', this) // 'this2' Window{...}
foo();
複製代碼

let const 不會被掛到 window

let x = 10
const y = 20

function foo () {
  console.log(this.x) // undefined
  console.log(this.y) // undefined
}

foo();
console.log(window.x) // undefined
複製代碼

看完有沒有感受 this 其實也並不難呢?來幾道綜合題鞏固一下吧!學習

綜合題

  1. 結合 DOM

    button.onClick = function(e) {
      console.log(this);
    }
    複製代碼

    答案不是 button,而是不肯定,由於都沒有調用,怎麼知道 this 呢?

    能夠這麼回答:由於沒有調用,因此沒法肯定 this,若是採用隱式調用的話,打印 button,若是使用顯示調用,則打印自定義的 this。

  2. 超難面試題

    let length = 10;
    function fn() {
      console.log(this.length);
    }
    
    let obj = {
      length: 5,
      method(fn){
      	fn()
        arguments[0]()
    	}
    }
    
    obj.method(fn, 1);
    複製代碼

    這道題有幾大坑:let、window.length、arguments.length

    因此答案也很是詭異

    不肯定(window.length = 頁面的iframe數量)
    2arguments.length = 實參長度,非形參)
    複製代碼

    關於 window.length 的值,能夠參見 MDN

總結

記住一「定」,二「轉」,三特例就行,核心是兩點:

  1. 調用時肯定 this
  2. 隱式調用上公式 obj.fn(1) = obj.fn.call(obj, 1)

這樣一來 this 就真相大白了!


參考文章

  1. 《this 的值究竟是什麼?一次說清楚》