JavaScript中的this—你不知道的JavaScript上卷讀書筆記(三)

來源:互聯網
上載者:User

標籤:fine   fun   其他   out   ror   情況   間接   屬性   ict   

this是什嗎?

this 是在運行時進行綁定的,並不是在編寫時綁定,它的上下文取決於函數調用時的各種條件。this 的綁定和函式宣告的位置沒有任何關係,只取決於函數的調用方式。當一個函數被調用時,會建立一個活動記錄(有時候也稱為執行內容)。這個記錄會包含函數在哪裡被調用(調用棧)、函數的調用方法、傳入的參數等資訊。this 就是記錄的其中一個屬性,會在函數執行的過程中用到。

調用位置與調用棧:

調用位置就是函數在代碼中被調用的位置(而不是聲明的位置)。

下面我們來看看到底什麼是調用棧和調用位置:

function baz() {    // 當前調用棧是:baz    // 因此,當前調用位置是全域範圍    console.log( "baz" );    bar(); // <-- bar 的調用位置}function bar() {    // 當前調用棧是baz -> bar    // 因此,當前調用位置在baz 中    console.log( "bar" );    foo(); // <-- foo 的調用位置}function foo() {    // 當前調用棧是baz -> bar -> foo    // 因此,當前調用位置在bar 中    console.log( "foo" );}baz(); // <-- baz 的調用位置
this綁定規則1. 預設綁定:
function foo() {    console.log( this.a );}var a = 2;foo(); // 2

上述代碼中,函數調用時應用了this 的預設綁定,因此this 指向全域對象(非strict 模式下)。

那麼我們怎麼知道這裡應用了預設綁定呢?可以通過分析調用位置來看看foo() 是如何調用的。在代碼中,foo() 是直接使用不帶任何修飾的函數引用進行調用的,因此只能使用預設綁定,無法應用其他規則。

如果使用strict 模式(strict mode),那麼全域對象將無法使用預設綁定,因此this 會綁定到undefined:

function foo() {    "use strict";    console.log( this.a );}var a = 2;foo(); // TypeError: this is undefined

這裡有一個微妙但是非常重要的細節,雖然this 的綁定規則完全取決於調用位置,但是只有foo() 運行在非strict mode 下時,預設綁定才能綁定到全域對象;strict 模式下與foo()的調用位置無關:

function foo() {    console.log( this.a );}var a = 2;(function(){    "use strict";    foo(); // 2})();
2. 隱式綁定:
function foo() {    console.log( this.a );}var obj = {    a: 2,    foo: foo};obj.foo(); // 2

當foo() 被調用時,它的落腳點指向obj 對象。當函數引用有內容物件時,隱式綁定規則會把函數調用中的this 綁定到這個內容物件。因為調用foo() 時this 被綁定到obj,因此this.a 和obj.a 是一樣的。

對象屬性引用鏈中只有最頂層或者說最後一層會影響調用位置

隱式丟失

一個最常見的this 綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用預設綁定,從而把this 綁定到全域對象或者undefined 上,取決於是否是strict 模式。

function foo() {    console.log( this.a );}var obj = {    a: 2,    foo: foo};var bar = obj.foo; // 函數別名!var a = "oops, global"; // a 是全域對象的屬性bar(); // "oops, global"

雖然bar 是obj.foo 的一個引用,但是實際上,它引用的是foo 函數本身,因此此時的bar() 其實是一個不帶任何修飾的函數調用,因此應用了預設綁定。

3. 顯式綁定:

就像我們剛才看到的那樣,在分析隱式綁定時,我們必須在一個對象內部包含一個指向函數的屬性,並通過這個屬性間接引用函數,從而把this 間接(隱式)綁定到這個對象上。那麼如果我們不想在對象內部包含函數引用,而想在某個對象上強制調用函數,該怎麼做呢?

JavaScript 中的“所有”函數都有一些有用的特性(這和它們的[[ 原型]] 有關——之後我們會詳細介紹原型),可以用來解決這個問題。具體點說,可以使用函數的call(..) 和apply(..) 方法。嚴格來說,JavaScript 的宿主環境有時會提供一些非常特殊的函數,它們並沒有這兩個方法。但是這樣的函數非常罕見,JavaScript 提供的絕大多數函數以及你自己建立的所有函數都可以使用call(..) 和apply(..) 方法。

function foo() {    console.log( this.a );}var obj = {    a:2};foo.call( obj ); // 2

通過foo.call(..),我們可以在調用foo 時強制把它的this 綁定到obj 上。

顯式綁定仍然無法解決我們之前提出的丟失綁定問題,通過以下方法可以解決:

硬綁定

    function foo() {        console.log( this.a );    }    var obj = {        a:2    };    var bar = function() {        foo.call( obj );    };    bar(); // 2    setTimeout( bar, 100 ); // 2    // 硬綁定的bar 不可能再修改它的this    bar.call( window ); // 2
4. new綁定:

使用new 來調用函數,或者說發生建構函式調用時,會自動執行下面的操作。

  1. 建立(或者說構造)一個全新的對象。

  2. 這個新對象會被執行[[ 原型]] 串連。
  3. 這個新對象會綁定到函數調用的this。

  4. 如果函數沒有返回其他對象,那麼new 運算式中的函數調用會自動返回這個新對象。

    function foo(a) {    this.a = a;}var bar = new foo(2);console.log( bar.a ); // 2
優先順序: new綁定>顯式綁定>隱式綁定判斷this:
  1. 函數是否在new 中調用(new 綁定)?如果是的話this 綁定的是新建立的對象。

    var bar = new foo()

  2. 函數是否通過call、apply(顯式綁定)或者硬綁定調用?如果是的話,this 綁定的是指定的對象。

    var bar = foo.call(obj2)

  3. 函數是否在某個內容物件中調用(隱式綁定)?如果是的話,this 綁定的是那個內容物件。

    var bar = obj1.foo()

  4. 如果都不是的話,使用預設綁定。如果在strict 模式下,就綁定到undefined,否則綁定到全域對象。

    var bar = foo()

綁定例外1. 被忽略的this

把null 或者undefined 作為this 的綁定對象傳入call、apply 或者bind,這些值在調用時會被忽略,實際應用的是預設綁定規則。

function foo() {    console.log( this.a );}var a = 2;foo.call( null ); // 2
更安全的ths

一種“更安全”的做法是傳入一個特殊的對象,把this 綁定到這個對象不會對你的程式產生任何副作用。。就像網路(以及軍隊)一樣,我們可以建立一個“DMZ”(demilitarized zone,非軍事區)對象——它就是一個空的非委託的對象。

function foo(a,b) {    console.log( "a:" + a + ", b:" + b );}// 我們的DMZ Null 物件var ? = Object.create( null ); //此處的?不會建立Object.prototype 這個委託,所以它比{}“更空”// 把數組展開成參數foo.apply( ?, [2, 3] ); // a:2, b:3// 使用bind(..) 進行柯裡化var bar = foo.bind( ?, 2 );bar( 3 ); // a:2, b:3
2. 間接引用

另一個需要注意的是,你有可能(有意或者無意地)建立一個函數的“間接引用”,在這種情況下,調用這個函數會應用預設綁定規則。

function foo() {    console.log( this.a );}var a = 2;var o = { a: 3, foo: foo };var p = { a: 4 };o.foo(); // 3(p.foo = o.foo)(); // 2
3. 軟綁定(此處不討論)this詞法(箭頭函數的this)
function foo() {    // 返回一個箭頭函數    return (a) => {        //this 繼承自foo()        console.log( this.a );    };}var obj1 = {    a:2};var obj2 = {    a:3};var bar = foo.call( obj1 );bar.call( obj2 ); // 2, 不是3 !

foo() 內部建立的箭頭函數會捕獲調用時foo() 的this。由於foo() 的this 綁定到obj1,bar(引用箭頭函數)的this 也會綁定到obj1,箭頭函數的綁定無法被修改。(new 也不行!)

JavaScript中的this—你不知道的JavaScript上卷讀書筆記(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.