JavaScript 捉摸不定的 This


Posted by Calon on 2022-04-26

this 大部份跟 function 怎麼宣告沒有關聯,跟怎麼調用 function 比較有關。

var someone = '全域';

function callSomeone (){
    console.log(this.someone);
};

callSomeone();
// 這邊是用 simple call 的方式調用 function
// 裡面的 this 會指向 window 中的 someone
// 所以這邊會印出 '全域'

const obj = {
    someone: '物件',
    callSomeone
};

obj.callSomeone();
// 這邊是 物件的方法調用
// 裡面的 this 會指向前面物件 obj 裡的 someone
// 所以這邊會印出 '物件'


const person = 'window';

function callPerson(){
    console.log(this.person);
};

callPerson();
// 這邊是用 simple call 的方式調用 function
// 裡面的 this 會指向 window 中的 person
// 但變數 person 宣告時是用 const,const 不會污染全域變數
// 所以 window 裡面沒有變數 person
// 這邊結果會是 undefine

const obj2 = {
    person: 'object',
    callPerson
};
obj2.callPerson();
// 這邊是 物件的方法調用
// 裡面的 this 會指向前面物件 obj2 裡的 person
// 所以這邊會印出 'object'

setTimeout(callSomeone, 1000);
(callPerson)();
// 這 2 個調用方式也是 simple call
// 所以 this 會指向 window
// 結果會是 undefine


比較好記的方式:看 function 前面的物件是誰,this 就會指向該物件。

function callSomeone (){
  console.log(this.someone);
};

const obj = {
  someone: '物件',
  callSomeone,
  innerObj: {
    someone: '內層物件',
    callSomeone,
    innermostObj: {
      someone: '深處物件',
      callSomeone,
    }
  }
};

obj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 obj
// 所以裡面的 this 會指向前面 obj
// 這邊會印出 '物件'

obj.innerObj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 innerObj
// 所以裡面的 this 會指向前面 innerObj
// 這邊會印出 '內層物件'

obj.innerObj.innermostObj.callSomeone();
// 這邊調用 callSomeone() 時前面的物件是 innermostObj
// 所以裡面的 this 會指向前面 innermostObj
// 這邊會印出 '深處物件'


影響 this 的語法

Javascript 有 3 種方法可以強制綁定 this 的指向,分別是:

  • call()
  • apply()
  • bind()

call()

call() 傳入參數後會立即執行 function。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(){
    console.log(this.myName);
  }
};


obj2.fn.call(obj1); // 'object1'

另外,call() 除了 this 參數以外也可以代入其他參數。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};


obj2.fn.call(obj1, 'Hi'); // 'Hi, object1'
// 這裡第 1 個參數是要綁定的 this 參數
// 第 2 個參數是 fn() 的參數

apply()

apply()call() 非常相似,不同的是 apply() 除了要綁定的 this 參數之外是代入 array。

fun.apply(thisArg, [argsArray])

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg1, msg2){
    console.log(msg1 + ', ' + this.myName + '. ' + msg2);
  }
};

obj2.fn.apply(obj1, ['Hi', 'How about you?']);
// 'Hi, object1. How about you?'

bind()

bind() 也和 call() 相似,差別在於 bind() 不會馬上執行函式,而是回傳綁定好 this 的 function。

var myName = 'window';

const obj1 = {
  myName: 'object1'
};

const obj2 = {
  myName: 'object2',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};


const newFn = obj2.fn.bind(obj1, 'Hi');
newFn();
// 'Hi, object1.

如果作為 this 的參數所傳入的值不是 Object

這時這些值會被轉為相關的建構方法:

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

fn.call(3); // this = new Number(3)
fn.apply('hi'); // this = new String('hi')
fn.bind(true)(); // this = new Boolean(true)


將 undefined、null 傳入 call()、apply()、bind()

當我們在執行 call()apply()bind() 時把綁定 this 的參數代入 undefined、null,這時 function 裡的 this 將會重新指向 window

var myName = 'window';

const obj = {
  myName: 'object',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};

obj.fn.call(undefined, 'Hi'); // 'Hi, window'
obj.fn.apply(undefined, ['Hi']); // 'Hi, window'
const newFn = obj.fn.bind(null, 'Hi');
newFn(); // 'Hi, window'
// 'Hi, object1.

而在嚴謹模式下則會出錯。

'use strict'
var myName = 'window';

const obj = {
  myName: 'object',
  fn(msg){
    console.log(msg + ', ' + this.myName);
  }
};

obj.fn.call(undefined, 'Hi');
// Uncaught TypeError:
// Cannot read properties of undefined (reading 'myName')


箭頭函式的 this

箭頭函式本身沒有 this,箭頭函式的 this 會指向外層作用域的 this

var person = 'window';

const callPerson = () => {
    console.log(this.person);
};

const obj = {
    person: 'object',
    callPerson
};
obj.callPerson();
// 箭頭函式的 this 會指向外層作用域的 this
// 這邊外層沒有作用域
// 所以這裡的 this 會指向 window


const obj2 = {
  person: 'object2',
  fn() {
    (() => {
      console.log(this.person);
    })();
  }
};

obj2.fn();
// obj2.fn 內的箭頭函式的 this 會指向 obj2.fn() 的 this
// 所以這邊會印出 'object2'

參考資料

#this #javascript







Related Posts

Markdown 常用格式

Markdown 常用格式

[day 02] new & factory: 如何建立一個新物件

[day 02] new & factory: 如何建立一個新物件

關於 React 小書:PropTypes 和 Component 參數驗證

關於 React 小書:PropTypes 和 Component 參數驗證


Comments