[筆記]認識 this 和 call、apply、bind


Posted by ianchen6501 on 2020-10-27

前言

在 JavaScript 中 this 常會因不同情況下代表不同的值,同時也想了解 call、apply 和 bind 的使用方法,所以做這個筆記。

在物件導向時

在物件導向(OOP)的環境中,this 會指向該類別(class)本身。

class flower {
  constructor(name) {
    this.name = name;
  }
  callName() {
    console.log(this.name);
  }
}

var sun_flower = new flower("sun_flower");
sun_flower.callName(); //"sun_flower"

用物件的方式呼叫

在 JavaScript 裡面 this 會因為 call 的方式不同會有不一樣的值,如果是用物件(object)的方式呼叫,會指向物件本身。

var obj = {
  myTeam: "arsenal",
  callMyTeam: function () {
    console.log(this.myTeam);
  },
};
obj.callMyTeam(); //"arsenal"

但要注意如果我們用函式的方法呼叫 this,會指向全域環境。

var myTeam = "chelsea";
var obj = {
  myTeam: "arsenal",
  callMyTeam: function () {
    console.log(this.myTeam);
  },
};
const callMyTeam = obj.callMyTeam; //注意這邊賦值了一個新的函式
callMyTeam(); //"chelsea"

用函式的方法來 call this

在非物件或物件導向的情況下,如果我們用一般函式的方法來 call this,this 會指向全域環境,所以依據全域環境(runtime)會回傳不同的值。
假若是瀏覽器會回傳 window,Node.js 則會回傳global

function test() {
    function inner() {
        console.log(this)
    }
    inner()
}
test()

上面的情況是在非"嚴格模式"的狀況下,但假設我們今天在"嚴格模式"下執行函式 call this,this 的值會變成 undefined。

'use strict';
function test() {
    function inner() {
        console.log(this)
    }
    inner()
}
test() //undefined

箭頭函式(arrow function)

用 arrow function 跟在一般 function 裡面 call this 會有不一樣的行為(可以當作一個特例),在 arrow function 裡面會回傳 this 原本建立作用域(scope)的賦值,沒有賦值就會回傳 undefined,而不是像一般 function 會回傳 runtime 環境或 undefined。

例如下面的範例,this 的 scope 是 obj 這個物件,所以 this.myTeam 就會回傳 obj 裡面的 MyTeam value。

this.myTeam = "arsenal";

const obj = {
  callMyTeam: () => {
    console.log(this.myTeam);
  },
};

obj.callMyTeam(); //"arsenal"

在拿另外一個例子來說,在 class 裡面的 scope 就是 class 本身。

class test {
  run() {
    console.log('run this:', this)
    setTimeout(() => {
      console.log(this)
    },180)
  }
}

const t = new test()
t.run() //run this: test {}, test{}

this 在 DOM 監聽裡面的回傳值

在瀏覽器 DOM 物件中 call this 會回傳正在執行的物件。

document.querySelector('#obj').addEventListener('click', () => {
  consol.log(this) //'obj'
})

知道了 this 在各個情況下所代表的值後,可以在繼續了解如何運用不同的 JavaScript method 來操作 this 囉。

先來談談 call、apply

call 和 apply 其實很相像,參數我們可以傳入 this 和其他參數,會回傳一個函式運算的結果,我們先來看看兩個的語法,

  1. call : fun.call(thisArg, arg1, arg2, ...)
  2. apply : fun.apply(thisArg, [argsArray])

可以看到兩個傳入的第一個參數都是 this 的值,差別在於後面的參數一個是傳入以,分開的不同參數,另一個則是陣列。

我們先來看看如何用 call 或 apply 來指定 this 的值

function stringnify() {
  return this.toString();
}

console.log(stringnify.call(1)); //"1"

如果要指定其他參數會變成如下列例子。

'use strict';
function test(a, b, c) {
  console.log(this)
    console.log(a ,b ,c)
}
test.call(123, 1, 2, 3) //回傳 123(this 的值) 1 2 3(其他參數)
test.apply(123, [1, 2, 3]) //回傳 123(this 的值) 1 2 3(其他參數)

接著再來看看 bind 的用法

function 可以用 bind 來綁定 this 的值,並且會建立一個新的函式,當該函式被呼叫的時候會取用 this 的值。bind 綁定後的函式就可以直接用ㄧ般 call function 的方式來使用。但注意的是之後 variable 的 this 值就不能再改變了。

我們先來看看 bind 的基本用法。

globalThis.name = "kevin";

var obj = {
  name: "michael",
  getName: function () {
    console.log(this.name);
  },
};

obj.getName(); //"michael","this 會指向 obj

var retrieveName = obj.getName;
const boudToName = retrieveName.bind(obj);
retrieveName(); //"kevin",this 會指向全域環境
boudToName(); //"michael","this 會指向 obj

在學過各種 this 代表的值及不同 method 之後,來做個小練習複習一下吧。

'use strict'

function log() {
  console.log(this);
}
//call, apply
var a = { a: 1, log: log };
var b = { a: 2, log: log };

log(); //嚴格模式 -> undefined
a.log(); //{a:1, log: log}
b.log.apply(a) //{a:1, log: log}
b.log.call(a) //{a:1, log: log}


//接著我們回顧一下 this 的不同 call 法會回傳什麼東西
const obj = {
  num: 1,
  test : function() {
    console.log(this)
  }
}


obj.test() // obj
const c = obj.test
c() //嚴格模式,undefined

//如果今天想要有一個函式可以綁定 obj 跟 obj.test ,我們可以用 bind()
const d = obj.test.bind(obj)
d() //obj

//但假如今天已經用 bind 綁定 this 的話,後面就算用 call 也無法更改了
d.call('123') //obj
tags: huli 的程式導師計畫 week16 物件導向 OOP this call apply bind

##this ##bind ##call ##apply







Related Posts

動態改變node CSS屬性 (補)

動態改變node CSS屬性 (補)

用 D3.js v4 看 Pokemon 屬性表

用 D3.js v4 看 Pokemon 屬性表

展開與其餘參數

展開與其餘參數


Comments