你應該要知道的JS中的this

前言

this 是 JavaScript 中不可不談的一個知識點,它很是重要但又不容易理解。由於 JavaScript 中的 this 不一樣於其餘語言。不一樣場景下的 this 指向不一樣(當函數被調用執行時會生成變量對象,肯定 this 的指向,所以當前函數的 this 是在函數被調用執行的時候才肯定的,因此致使 this 的指向靈活不肯定),並且,在嚴格模式和非嚴格模式下,this 也會有不一樣的解讀。html

爲何要有 this

先想一想若是 JavaScript 中沒有 this 會怎麼樣?好比下面這段代碼:數組

function identity(context) {
    return context.name.toUpperCase();
}
function speak(context) {
    var greeting = 'Hello, I am ' + identity(context)
    console.log(greeting)
}
var you = {
    name: 'Reader'
}
var me = {
    name: 'Stone'
}
identity(you); // READER
speak(me); // Hello, I am Stone

咱們給這 identity 和 speak 兩個函數顯示的傳入了一個上下文對象,這彷佛看不出什麼,可是一旦你的應用變得愈來愈複雜,這種顯示傳遞上下文就會讓代碼愈來愈混亂,代碼結構愈來愈模糊。而使用 this 就能夠避免這樣,由於 this 提供了一種更優雅的方式來隱式傳遞對象引用,能夠把 API 設計得更加簡潔易用。瀏覽器

this 的綁定規則

全局對象中的 this

全局對象的變量對象是一個比較特殊的存在,在全局對象中,this 指向它自己,好比:app

// this 綁定到全局對象
this.a1 = 10;

// 經過聲明綁定到變量對象,全局環境中,變量對象就是它自己
var a2 = 20;

// 會隱式綁定到全局對象
a3 = 30;

console.log(a1); // 10
console.log(a2); // 20
console.log(a3); // 30

函數中的 this

在一個函數的執行上下文中,this 由該函數的調用者提供,由函數的調用方式來決定 this 的指向。下面這個例子:ide

function fn() {
    console.log(this)
}
fn(); // Window {...}

默認全局對象就是調用者,等價於 window.fn()(只討論瀏覽器中全局對象)。可是在非嚴格模式中,this 是指向 undefined 的,好比:函數

'use strict';
function fn() {
    console.log(this);
}
fn(); // undefined
window.fn(); // Window {...}

這就說明了,若是不指定函數調用者,在嚴格模式下回默認綁定到全局對象,在非嚴格模式下默認指向 undefined 。this

函數是獨立調用,仍是被某個對象所調用,是很容易分辨的,好比:設計

'use strict';
var a = 20;
function fn() {
    var a = 1;
    var obj = {
        a: 10,
        c: this.a + 20
    }
    return obj.c
}
console.log(window.fn()); // 40
console.log(fn()); // TypeError

對象字面量的形式不會產生本身的做用域,因此 obj 中的 this.a 並非指向 obj ,而是與函數內部的 this 同樣。所以,當 window.fn() 調用時,fn 內部的 this 指向 window 對象,此時 this.a 訪問全局對象中的 a ;因爲是在嚴格模式中,在沒有指明調用者的時候,fn 內部默認指向 undefined,因此在單獨調用的時候會報錯。code

call/apply/bind 顯示綁定 this

JavaScript 中提供了一個方式可讓咱們手動指定函數內部的 this 指向,也就是前面提到的隱式傳遞對象,它們就是call/apply/bind。這三個方法是 Function 的原型方法,全部函數均可以調用這三個方法。看下面這個例子:htm

var a = 20;
var obj = {
    a: 40
}
function fn() {
    console.log(this.a);
}
fn(); // 20
fn.call(obj); // 40
fn.apply(obj); // 40

當函數調用 apply/bind 時,表示執行該函數,而且這個函數內部的 this 指向 apply/bind 的第一個參數。

兩者的區別:

  • call 的第一個參數是函數內部 this 的指向,後續的參數則是函數執行時所須要的參數,一個一個傳遞
  • apply 的第一個參數與 call 相同,而執行函數的參數,則以數組的形式傳入

bind 方法也能指定函數的 this ,可是它不一樣於call/apply。bind 方法會返回一個新函數,這個函數與原函數有相同的函數體,可是函數內部的 this 被綁定成 bind 方法的第一個參數,後續參數也是一個一個傳入,而且不會自動執行新函數。

構造函數中的 this

能夠把構造函數當作是普通函數,其中的 this 指向是建立的對象實例,之因此稱之爲構造函數,是由於咱們會藉助 new 操做符來調用函數。在用 new 建立對象時,this 指向發生改變是在第二步:

  1. 建立一個對象實例
  2. 將構造函數中的 this 指向這個對象
  3. 執行構造函數中的代碼
  4. 返回這個新建立的對象
function Foo() {
    this.a = 20
}
var foo = new Foo()
console.log(foo.a) // 20

箭頭函數中的 this

箭頭函數內部是不會綁定 this 的,它會捕獲外層做用域中的 this,做爲本身的 this 值。好比:

function Person() {
  this.age = 20;
  (() => {
    this.age++
  })()
}
var p = new Person()
console.log(p.age)

DOM事件中的 this

當函數被當作監聽事件處理函數時, 其 this 指向觸發該事件的元素 (針對於addEventListener事件)。好比:

<div class="box">
    click
</div>
document.querySelector('.box').addEventListener('click', function(e) {
  console.log(this) // 這個this指向div.box元素
}, false)

總結

this 的使用場景豐富多樣,能夠用來實現繼承,實現函數柯里化等,做爲開發者應該清楚各類使用方式以及其內部原理。

參考

你不知道的JavaScript(上卷)

相關文章
相關標籤/搜索