JavaScript 設計模式與開發實踐 - Chapter 2

April 14, 2016

Chapter 2

一般在物件導向中,我們知道 this 是指向到一個類別,但是在 JavaScript 中,this 指向的是一個物件, 而實際上到底是哪個物件,它是基於 function 執行時所執行的環境所綁定的,並不是 function 被宣告時當下的環境。

this 的指向可以分為四種:

  1. 作為物件的呼叫方法。
  2. 作為普通 function 的呼叫。
  3. 建構子的呼叫。
  4. Function.prototype.callFunction.prototype.apply 的呼叫。

接下來透過範例來了解這四種呼叫方式:

作為物件的呼叫方法

const obj = {
  name: 'pj',
  sayHi: function() {
    console.log(this === obj) // true
    console.log(`Hi, ${this.name}`); // pj
  },
};

作為普通 function 的呼叫

當 function 不作為物件被呼叫時,也就是普通 function,這時候的 this 指向的是全域物件,在 JavaScript 中指的就是 window

window.name = 'pj';

const sayHi = function () {
  return `Hi, ${this.name}`;
};

console.log(sayHi()); // pj

建構子呼叫

const P = function {
  this.name = 'pj';
};

const p = new P();
console.log(p.name) // pj

另一個特別的情況是:

const P = function () {
  this.name = 'pj';
  return {
    name: 'other people',
  };
}

const p = new P();
console.log(p.name) // other people

所回傳的並不是我們原本所給的 this.name,而是一個物件。

Function.prototype.call 和 Function.prototype.apply

Function.prototype.callFunction.prototype.apply 兩者的方式其實差不多,第一個參數都是傳入 function 內的 this,差別在於後面第二個所接受的參數不同,更多請參考 JavaScript MDN

這兩個方法可以動態的去改變傳入 function 的 this

const obj1 = {
  name: 'obj1',
  getName: function () {
    return `obj1: ${this.name}`;
  },
};

const obj2 = {
  name: 'obj2',
  getName: function () {
    return `obj2: ${this.name}`;
  }
};

console.log(obj1.getName.call(obj2));

透過 applycall 這兩個方法,可以改變 function 內 this 的指向,有時候在寫程式會不小心把 this 所指向的地方改掉了,造成一些執行錯誤,applycall 可以幫忙我們改善這個問題。

Function.prototype.bind

在 Reactjs 中還蠻常用到 bind 這個方法,其實 bind 的實作也是利用了 apply 的功能,下面範例是一個簡單的實作:

Function.prototype.bind = function (context) {
  var self = this;
  return function () {
    return self.apply(context, arguments);
  }
};

const obj = {
  name: 'pj'
};

const myFunc = function() {
  console.log(this.name);
}.bind(obj);

myFunc(); // pj