this 是 JavaScript 中不可不談的一個知識點,它很是重要但又不容易理解。由於 JavaScript 中的 this 不一樣於其餘語言。不一樣場景下的 this 指向不一樣(當函數被調用執行時會生成變量對象,肯定 this 的指向,所以當前函數的 this 是在函數被調用執行的時候才肯定的,因此致使 this 的指向靈活不肯定),並且,在嚴格模式和非嚴格模式下,this 也會有不一樣的解讀。html
先想一想若是 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 指向它自己,好比:app
// this 綁定到全局對象 this.a1 = 10; // 經過聲明綁定到變量對象,全局環境中,變量對象就是它自己 var a2 = 20; // 會隱式綁定到全局對象 a3 = 30; console.log(a1); // 10 console.log(a2); // 20 console.log(a3); // 30
在一個函數的執行上下文中,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
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 的第一個參數。
兩者的區別:
bind 方法也能指定函數的 this ,可是它不一樣於call/apply。bind 方法會返回一個新函數,這個函數與原函數有相同的函數體,可是函數內部的 this 被綁定成 bind 方法的第一個參數,後續參數也是一個一個傳入,而且不會自動執行新函數。
能夠把構造函數當作是普通函數,其中的 this 指向是建立的對象實例,之因此稱之爲構造函數,是由於咱們會藉助 new 操做符來調用函數。在用 new 建立對象時,this 指向發生改變是在第二步:
function Foo() { this.a = 20 } var foo = new Foo() console.log(foo.a) // 20
箭頭函數內部是不會綁定 this 的,它會捕獲外層做用域中的 this,做爲本身的 this 值。好比:
function Person() { this.age = 20; (() => { this.age++ })() } var p = new Person() console.log(p.age)
當函數被當作監聽事件處理函數時, 其 this 指向觸發該事件的元素 (針對於addEventListener事件)。好比:
<div class="box"> click </div>
document.querySelector('.box').addEventListener('click', function(e) { console.log(this) // 這個this指向div.box元素 }, false)
this 的使用場景豐富多樣,能夠用來實現繼承,實現函數柯里化等,做爲開發者應該清楚各類使用方式以及其內部原理。
參考