亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

全部開發者教程

JavaScript 入門教程

JavaScript this

當前執行代碼的環境對象,在非嚴格模式下,總是指向一個對象,在嚴格模式下可以是任意值。(MDN)

this 指向的是當前的代碼上下文環境,所以不同情況下的 this 指向也不同。

1. 全局下的 this

在全局環境下,this 指向全局對象。

全局對象和宿主環境相關,在瀏覽器下,全局對象就是 window 對象,在 node.js 中,全局對象是 global 對象。

window === this; // 輸出:true

圖片描述

新的標準提供了 globalThis 關鍵字來獲取全局對象,這樣就能抹平宿主的差異來操作處理全局對象了。

2. 函數中的 this

函數在不同情況下,其 this 的指向也不同。

2.1 對象下的方法

方法也是一個函數,如果通過對象調用一個函數,函數的 this 就會指向這個對象。

var person = {
  age: 14,
  name: '鴿子王',
  skill: '放鴿子',
  say: function() {
    console.log('來一段自我介紹:');
    console.log('我是' + this.name);
    console.log('我今年' + this.age + '歲');
    console.log('我最擅長' + this.skill);
  },
};

person.say();

圖片描述

say函數作為對象下的方法,在被調用后,其 this 指向的是他所在的對象,在這里就是 person 對象。

2.2 原型鏈上方法的 this

原型鏈上的方法,this 指向的也是調用該方法的對象。

var __proto__ = {
  sum: function() {
    return this.number1 + this.number2;
  },
};

var object = Object.create(__proto__);

object.number1 = 1;
object.number2 = 2;

console.log(
  object.sum(),
); // 輸出:3

圖片描述

Object.create 做就就是將參數作為原型,創建一個對象。

所以 object 的第一原型就是 __proto__ 對象。

number1number2 都是 object 變量的屬性,但卻可以被 sum 方法中的 this 訪問到,所以在原型鏈的方法中,this 指向的就是調用該方法的對象。

2.3 getter / setter 下的 this

gettersetter 下的 this 也會指向調用該 gettersetter 的對象。

var object = {
  _name: '鴿子王',

  get name() {
    return this._name;
  },

  set name(val) {
    console.log(val);
    this._name = val;
  }
};

console.log(object.name); // 輸出:鴿子王

object.name = '鴿子天王'; // 輸出:鴿子天王

console.log(object.name); // 輸出:鴿子天王

圖片描述

gettersetter 本質上也可以理解成兩個函數,作為對象下的函數,在調用的時候 this 也會指向該對象。

2.4 作為 DOM 節點的事件處理器

作為 DOM 節點的事件處理器的時,函數的 this 會指向這個 DOM 對象。

<div>
  <button>點擊我</button>
</div>

<script>
  document.querySelector('button').addEventListener('click', function() {
    this.innerHTML = '被點擊了!';
  });
</script>

圖片描述

2.5 作為一個內聯的事件處理器

內聯的事件處理器,其 this 指向的是 DOM 節點自身。

<div>
  <button onclick="console.log(this); console.log(this === document.querySelector('button'))">點擊我</button>
</div>

圖片描述

這個規則有局限性,只有最外層的 this 符合這個規則。

<div>
  <button onclick="function test() { console.log(this) }; test();">點擊我</button>
</div>

test 函數的 this 指向的是全局對象 window

圖片描述

2.6 其他大部分情況下

排開上述的幾個情況,剩下的函數大部分情況下在調用時,this 指向的是全局對象,在瀏覽器中就是 window 對象。

function fn() {
  console.log(this);

  console.log(this === window);
}

fn();

這樣調用函數,其 this 指向的就是 window 對象了。

有的時候可能會搞混以下情況:

var object = {
  username: '咸魚',
  fn: function() {
    console.log(this.username);

    function thisTest() {
      console.log(this.username);

      console.log(this === window);
    }

    thisTest();
  },
};

object.fn();

圖片描述

這里 thisTest 方法輸出的 username 就會是個 undefined,因為他的 this 指向的是 window,因為他不屬于 object 對象的一個方法,所以 this 就指向了 window。

在回調函數中經常會碰到這個問題:

var info = {
  account: '123',
  password: '456',
  login: function(cb) {
    setTimeout(function() {
      cb({
        account: this.account,
        password: this.password,
      });
    }, 1000);
  }
};

info.login(function(info) {
  console.log(info);
});

圖片描述

這里回調函數獲取的賬號和密碼是 undefined,原因就是 this 的指向問題。

通常會使用保留上層 this 的方式解決這個問題。

var info = {
  account: '123',
  password: '456',
  login: function(cb) {
    var _this = this;

    setTimeout(function() {
      cb({
        account: _this.account,
        password: _this.password,
      });
    }, 1000);
  }
};

info.login(function(info) {
  console.log(info);
});

圖片描述

這樣就能解決這個問題。

另外一個情況也很容易混淆 this :

var object = {
  user: 'no.1',
  say: function() {
    console.log(this.user);
  },
};

var say = object.say;

object.say(); // 輸出:"no.1"
say(); // 輸出:undefined

圖片描述

這是因為把 object 下的 say 方法單獨賦值給 say 變量的時候,其就作為了 window 下的一個方法,所以他的 this 指向的是 window。

在嚴格模式中,這種情況下的 this 會變成 undefined。

2.7 構造函數

在 JavaScript 構造函數也被成為 對象構造器,用于產生對象。

構造函數的聲明和普通函數幾乎沒有區別:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var point = new Point(1, 2);

console.log(point.x); // 輸出:1
console.log(point.y); // 輸出:2

構造函數使用 new 關鍵字來構造對象。所以當一個函數被使用 new 關鍵字調用時,這個函數就會作為一個構造函數。

在一個構造函數被調用后,其內部的 this 會指向一個對象,具體的內容可以參考 構造函數 章節。

3. 修改this

3.1 call 方法和 apply 方法

函數具有 call 方法和 apply 方法,這兩個方法可以在調用函數的時候指定函數的 this。

var object = {
  user: 'no.1',
};

function say() {
  console.log(this.user);
}

say(); // 輸出:undefined
say.call(object); // 輸出:"no.1"
say.apply(object); // 輸出:"no.1"

圖片描述

通過 callapply 方法將 say 函數執行時候的 this 設置為 object 對象。

call 方法從第二個參數開始,表示是要傳遞給當前函數的參數。

var object = {
  user: 'no.1',
};

function fn(arg1, arg2, arg3) {
  console.log(
    this,
    arg1,
    arg2,
    arg3,
  );
}

fn.call(object, 1, 2, 3);

圖片描述

apply 的第二個參數是個數組,數組里面的項會按數組的順序作為參數傳遞給函數。

var object = {
  user: 'no.1',
};

function fn() {
  console.log(
    this,
    arguments,
  );
}

fn.apply(object, [1, 2, 3]);

圖片描述

通過 arguments 關鍵字就可以看到當前函數的參數,通常在需要修改 this ,又不確定參數的情況下,會使用 apply 來修改 this。

3.2 bind

bind 方法用于給一個函數永久綁定一個指定的 this,bind 不會修改原函數,會返回一個新的函數。

var obj1 = { value: '今天打磚' };
var obj2 = { value: '明天打轉' };

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

var bindFn1 = fn.bind(obj1)
var bindFn2 = bindFn1.bind(obj2);

bindFn1();
bindFn2();

圖片描述

可以看到 bindFn1 被綁定了 obj1 作為 this,之后不論怎么操作,他的 this 都會是 obj1。

bind 還有更多靈活的用法,參數也可以綁定,有關 bind、call、apply 這三個方法的更詳細的信息可以查閱對應的文檔。

4. 小結

理解好 this 的處理機制可以設計出更加完善的 JavaScript 應用程序。

this 在 ES6 的箭頭函數中的表現也有所不同,可以查閱 ES6 中有關箭頭函數的內容。