JavaScript 核心觀念(42)-函式以及 This 的運作-this:call, apply, bind 與 嚴謹模式
前言
接下來將會介紹 this
很特別的東西,也就是 call
、apply
以及 bind
,最後也會提到嚴謹模式對於 this
的用途是什麼。
call, apply 以及 bind
首先這章節將會介紹「call
, apply
以及 bind
」這三個方法,而這三個方法其實會影響 this
的呼叫,但這邊要注意,這三個方法其實觀念都是非常接近的,因此就非常容易混淆。
首先先看基本的範例程式碼
1 | var obj = { |
在此上方是我們前面章節的 simalp call 狀況,因此 this
就會指向 Global,那麼當我們希望 this
重新指向到 obj
的話該怎麼做呢?首先我們可以先使用 call
。
這邊先從 MDN 對於 call
的說明,以下擷取 MDN 說明
你可以在呼叫一個現存的函數時,使用不一樣的 this 物件。 this 會參照到目前的物件,呼叫的物件上。
這是什麼意思呢?簡單來講我們可以指定 this
的指向,而寫法如下:
1 | var obj = { |
此時就可以發現 this
的指向到 obj
了,除此之外假設若函式本身還有其他參數要傳入,那麼就可以接著寫在 calll
之後,因此 call
第一個參數就會是 this
想要指向的地方,而這之後則會是函式的傳入參數
1 | var obj = { |
接下來是 apply
,apply
其實也是類似的觀念,因此寫法也可以改成這樣
1 | var obj = { |
但是 apply
與 call
還是有差異的,首先差異是什麼呢?在 call
的後方是以參數的形式傳入,而 apply
則必須採用陣列方式傳入函式的參數
1 | var obj = { |
而這邊我也擷取 MDN 對於 apply 的說明
apply 與 call() 非常相似,不同的是支援的傳入參數類型。使用陣列形式的參數,而不是命名過的接收參數。
最後來說一下 bind
,首先 bind
與 apply
、call
最大差異在於,bind
並不會立刻執行,而 apply
、call
是會立刻執行的,這邊是什麼意思呢?來看一下範例程式碼
1 | var obj = { |
當如果你將上方程式碼貼入到瀏覽器的 console
你會發現只會出現 fn
函式的本身而不會呼叫函式,因此就必須宣告一個變數並呼叫該函式才可以
1 | var obj = { |
因此可以看到當我們呼叫 fn2()
時,bind
它就會將 this
指向到 obj
中。
除此之外 bind
還有一個特定就是,當你已經傳入特定參數之後,後來想再傳入其他參數是無法的
1 | var obj = { |
而這部分的規則很簡單,只要你有先傳入參數,那麼 bind
就會自動幫你鎖定參數,因此若你不想一開始就傳入特定參數,而是希望後來才傳入,那麼就可以改成以下
1 | var obj = { |
在此前面章節範例其實我們都是透過傳入一個物件給 this
,因此其實傳入純值也是可以的
1 | function fn (a, b) { |
基本上這邊看到這個結果應該會有點特別,this
出來是一個建構式的方式去呈現,但是這邊有一個好玩的地方就是,如果你傳入的是 undeinfed
,那麼 this
就會改指向 window
1 | function fn (a, b) { |
那麼是什麼原因造成這種狀況呢?其實 MDN 有說明
注意,它可能是一個無法在函數內看到的值:若這個函數是在非嚴苛模式( non-strict mode ), null 、undefined 將會被置換成全域變數,而原生型態的值將會被封裝
因此這就是為什麼 this
會指向到 window
與出現的是一個建構式。
嚴格模式
接下來來講一下什麼是嚴格模式
嚴格模式是什麼呢?由於 JavaScript 在開發上是相對寬鬆且自由的程式語言,因此在開發上就很容易發生一些問題,那麼嚴格模式就是在避免我們發生這些狀況,以避免會出現問題的程式碼還是可以運作
那這個最主要特色在什麼呢?例如當你 this
如果是指向 window
那麼就會出現錯誤,例如 sample call 會變成指向到 undefined
1 |
|
而變數的話若沒有給予出初值則會出現「VM543:3 Uncaught ReferenceError: a is not defined
」
1 |
|
除此之外,使用 'use strict'
若在 ES6 ES7 ES8 等未來版本可能會使用的語法,你也會被禁止使用,避免佔用該語法。
'use strict'
還有一個特別的地方,也就是它必須在程式碼最前面才有效,否則以下都無效
1 | function fn () { |
但我們可以用執行環境來限制
1 | function fn () { |
因此嚴格模式是依照執行環境來限制的。
而嚴格模式對於 call
也有一個幫助,它可以避免 this
變成建構式
1 | function fn (a, b) { |
最後這邊也可以參考一下 MDN 對於嚴格模式的說明。