首發地址: https://github.com/jeuino/Blo...
本篇文章將介紹 JavaScript 執行上下文中,最後一個重要的屬性 —— this。javascript
在《JavaScript 之做用域與做用域鏈》中,介紹了詞法做用域與動態做用域,JavaScript 是基於詞法做用域的,而 this 的行爲卻與動態做用域相似,this 的指向是基於調用棧的。html
this 是在運行時進行綁定的,而不是在編寫時綁定,它的指向取決於函數是怎麼被引用的。java
this 綁定的類型能夠分爲如下幾類:git
咱們直接經過例子來看 this 是如何綁定的。(多例預警,部分示例參考書籍《你不知道的JavaScript》)github
eg1.1:數組
function foo() { console.log(this.a); } var a = 1; foo(); // 1
輸出:1
緣由:
app
若是你不清楚變量和函數在內存中是如何存儲的,請閱讀 《JavaScript 以內存空間》
經過 foo()
調用函數時:解釋器首先會在棧中找到 foo 函數的引用地址,而後根據引用地址到堆中找到函數並執行。這種直接在詞法做用域中查找函數引用並調用的方式,this 會指向全局對象 window。函數
注意:在嚴格模式下,this 的值爲 undefined。此處的嚴格模式是指,函數體處於嚴格模式下,this 的值爲 undefined。不然 this 會被綁定到 Window 對象。ui
eg1.2:鞏固例,輸出結果的緣由自行分析呦this
function foo2() { console.log(this.a); } function foo() { var a = 'foo'; foo2(); } var a = 1; foo(); // 1
eg2.1:
function foo() { console.log(this.a); } var obj = { a: 1, foo: foo }; var a = 'global' obj.foo(); // 1
輸出:1
緣由:
經過 obj.foo()
調用函數時:解釋器首先會在棧中找到 obj 對象的引用地址,而後根據引用地址到堆中找到原始對象,而後再根據 obj.foo 屬性值(屬性值爲 foo 函數引用地址)在堆中找到函數並執行。
foo 函數是經過對象 obj
來引用的,也就是說,它的調用位置是 obj 對象,因此 this 指向 obj 對象。
eg2.2:鞏固例,輸出結果的緣由自行分析呦
function foo() { console.log(this.a); } var obj1 = { foo: foo, a: 1 }; var obj2 = { obj1: obj1, a: 2 }; obj2.obj1.foo(); // 1
eg3.1:
function foo() { console.log(this.a); } var obj = { foo: foo, a: 1 }; var a = 'global'; var bar = obj.foo; bar(); // 'global'
輸出:'global'
緣由:
我想你們經過上圖應該就已經明白爲何輸出的是'global'
了。(參考 eg1.1)
eg3.2:鞏固例,輸出結果的緣由自行分析呦
function foo() { console.log(this.a); } function doFoo(fn) { fn(); } var obj = { foo: foo, a: 1 }; var a = 'global'; doFoo(obj.foo); // 'global'
總結:
隱式綁定有兩種形式:
顯示綁定 this 的幾種方法:
舉例說明:
function foo() { console.log(this.a); } var obj = { a: 1 }; foo.call(obj); // 1 // or foo.apply(obj); // 1
經過 call
或 apply
方法,能夠在調用 foo 時,強制將它的 this 綁定到 obj 上。
call
和apply
方法的功能是同樣的,它們的區別在於傳遞參數的格式不一樣。call
逐個傳遞單個參數(foo.call(obj,1,2,3)
),apply
一次性傳遞一個參數數組(foo.apply(obj,[1,2,3])
)。若是 call 和 apply 中傳入的第一個參數是原始值(eg: 'str', true, 1),那麼這個原始值會被轉換成它的對象形式(也就是 new String()、new Boolean()、new Number() ..),這個過程一般被稱爲 「裝箱」 操做。
使用 bind(...) 方法,會返回一個指定 this 後的函數,該函數可重複使用。
舉例說明:
function foo() { console.log(this.a); } var obj = { a: 1 }; var bar = foo.bind(obj); bar(); // 1
若是將 null 或者 undefined 做爲 this 的綁定對象傳入到 call/apply/bind 中,此時 this 指向 window。
使用 new 操做符來調用函數時,也就是咱們常常說的發生構造函數調用時,會進行如下操做:
function Foo(a) { this.a = a; } var obj = new Foo(1); console.log(obj.a); // 1
簡單來講,就是使用 new 操做符來調用函數 Foo 時,會建立一個新的對象並把它綁定到 Foo 函數執行上下文中的 this 上。
以上四條 this 綁定規則的優先級以下:
咱們再來思考一個例子:
function foo() { console.log(this.a); } var a = 1; var obj = { a: 'obj', foo: foo }; var bar = { a: 'bar' }; (bar.foo = obj.foo)(); // 1
賦值表達式 bar.foo = obj.foo
的返回值是 obj.foo
的值,也就是目標函數 foo 的引用。所以,調用位置等價於 foo()
,因此這裏應用的是默認綁定,輸出結果爲 1。
在每一個執行上下文中,都包括三個重要的屬性:
截止到此篇文章,執行上下文中的三個屬性就都介紹完啦。下篇文章將開始介紹執行上下文的生命週期,敬請期待。
參考:
JavaScript 的 this 原理 《你不知道的 JavaScript》上