函式進階
函式的定義方式
-
函式宣告
-
函式表示式
-
new Function
函式宣告
function foo () {
}
函式表示式
var foo = function () {
}
函式宣告與函式表示式的區別
-
函式宣告必須有名字
-
函式宣告會函式提升,在預解析階段就已建立,宣告前後都可以呼叫
-
函式表示式類似於變數賦值
-
函式表示式可以沒有名字,例如匿名函式
-
函式表示式沒有變數提升,在執行階段建立,必須在表示式執行之後才可以呼叫
下面是一個根據條件定義函式的例子:
if (true) { function f () { console.log(1) } }else { function f () { console.log(2) } }
以上程式碼執行結果在不同瀏覽器中結果不一致。
不過我們可以使用函式表示式解決上面的問題:
var f if (true) { f = function () { console.log(1) } } else { f = function () { console.log(2) } }
函式的呼叫方式
-
普通函式
-
建構函式
-
物件方法
函式內 this
指向的不同場景
函式的呼叫方式決定了 this
指向的不同:
呼叫方式 | 非嚴格模式 | 備註 |
---|---|---|
普通函式呼叫 | window | 嚴格模式下是 undefined |
建構函式呼叫 | 例項物件 | 原型方法中 this 也是例項物件 |
物件方法呼叫 | 該方法所屬物件 | 緊挨著的物件 |
事件繫結方法 | 繫結事件物件 | |
定時器函式 | window |
這就是對函式內部 this 指向的基本整理,寫程式碼寫多了自然而然就熟悉了。
函式也是物件
-
所有函式都是
Function
的例項
call、apply、bind
那瞭解了函式 this 指向的不同場景之後,我們知道有些情況下我們為了使用某種特定環境的 this 引用,這時候時候我們就需要採用一些特殊手段來處理了,例如我們經常在定時器外部備份 this 引用,然後在定時器函式內部使用外部 this 的引用。然而實際上對於這種做法我們的 JavaScript 為我們專門提供了一些函式方法用來幫我們更優雅的處理函式內部 this 指向問題。這就是接下來我們要學習的 call、apply、bind 三個函式方法。
call
call()
方法呼叫一個函式, 其具有一個指定的 this
值和分別地提供的引數(引數的列表)。
<p class="danger"> 注意:該方法的作用和 apply()
方法類似,只有一個區別,就是 call()
方法接受的是若干個引數的列表,而 apply()
方法接受的是一個包含多個引數的陣列。</p>
語法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
引數:
-
thisArg
-
在 fun 函式執行時指定的 this 值
-
如果指定了 null 或者 undefined 則內部 this 指向 window
-
-
arg1, arg2, ...
-
指定的引數列表
-
apply
apply()
方法呼叫一個函式, 其具有一個指定的 this
值,以及作為一個數組(或類似陣列的物件)提供的引數。
<p class="danger"> 注意:該方法的作用和 call()
方法類似,只有一個區別,就是 call()
方法接受的是若干個引數的列表,而 apply()
方法接受的是一個包含多個引數的陣列。</p>
語法:
fun.apply(thisArg, [argsArray])
引數:
-
thisArg
-
argsArray
apply()
與 call()
非常相似,不同之處在於提供引數的方式。apply()
使用引數陣列而不是一組引數列表。例如:
fun.apply(this, ['eat', 'bananas'])
bind
bind() 函式會建立一個新函式(稱為繫結函式),新函式與被調函式(繫結函式的目標函式)具有相同的函式體(在 ECMAScript 5 規範中內建的call屬性)。當目標函式被呼叫時 this 值繫結到 bind() 的第一個引數,該引數不能被重寫。繫結函式被呼叫時,bind() 也接受預設的引數提供給原函式。一個繫結函式也能使用new操作符建立物件:這種行為就像把原函式當成構造器。提供的 this 值被忽略,同時呼叫時的引數被提供給模擬函式。
語法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
引數:
-
thisArg
-
當繫結函式被呼叫時,該引數會作為原函式執行時的 this 指向。當使用new 操作符呼叫繫結函式時,該引數無效。
-
-
arg1, arg2, ...
-
當繫結函式被呼叫時,這些引數將置於實參之前傳遞給被繫結的方法。
-
返回值:
返回由指定的this值和初始化引數改造的原函式拷貝。
示例1:
this.x = 9; var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 返回 81 var retrieveX = module.getX; retrieveX(); // 返回 9, 在這種情況下,"this"指向全域性作用域 // 建立一個新函式,將"this"繫結到module物件 // 新手可能會被全域性的x變數和module裡的屬性x所迷惑 var boundGetX = retrieveX.bind(module); boundGetX(); // 返回 81
示例2:
function LateBloomer() { this.petalCount = Math.ceil(Math.random() * 12) + 1; } // Declare bloom after a delay of 1 second LateBloomer.prototype.bloom = function() { window.setTimeout(this.declare.bind(this), 1000); }; LateBloomer.prototype.declare = function() { console.log('I am a beautiful flower with ' + this.petalCount + ' petals!'); }; var flower = new LateBloomer(); flower.bloom(); // 一秒鐘後, 呼叫'declare'方法
小結
-
call 和 apply 特性一樣
-
都是用來呼叫函式,而且是立即呼叫
-
但是可以在呼叫函式的同時,通過第一個引數指定函式內部
this
的指向 -
call 呼叫的時候,引數必須以引數列表的形式進行傳遞,也就是以逗號分隔的方式依次傳遞即可
-
apply 呼叫的時候,引數必須是一個數組,然後在執行的時候,會將陣列內部的元素一個一個拿出來,與形參一一對應進行傳遞
-
如果第一個引數指定了
null
或者undefined
則內部 this 指向 window
-
-
bind
-
可以用來指定內部 this 的指向,然後生成一個改變了 this 指向的新的函式
-
它和 call、apply 最大的區別是:bind 不會呼叫
-
bind 支援傳遞引數,它的傳參方式比較特殊,一共有兩個位置可以傳遞
-
在 bind 的同時,以引數列表的形式進行傳遞
-
-
在呼叫的時候,以引數列表的形式進行傳遞
-
-
那到底以誰 bind 的時候傳遞的引數為準呢還是以呼叫的時候傳遞的引數為準
-
兩者合併:bind 的時候傳遞的引數和呼叫的時候傳遞的引數會合併到一起,傳遞到函式內部
-