函數(shù)是對象金抡,函數(shù)可以存放在變量中举农,作為參數(shù)傳遞荆针。函數(shù)可以返回函數(shù)。因為函數(shù)也是對象颁糟,因此函數(shù)可以擁有方法航背。
定義
ES6 之前的函數(shù)定義可以通過函數(shù)表達(dá)式或函數(shù)語句,分別是:
var test = function() {}
function test2() {}
函數(shù)語句在解析時會被提升:不管函數(shù)語句放置在哪里棱貌,它會被移動到所在作用域的頂部玖媚。可使得函數(shù)不必先聲明后使用婚脱。
ES6 新增了箭頭函數(shù):
let odds = evens.map(v => v + 1)
=>
之前是函數(shù)參數(shù)列表今魔。如果只有一個參數(shù)可以省略括號。如果沒有參數(shù)障贸,必須加空的括號错森。多個參數(shù)也必須使用括號。
let test1 = v => v + 1
let test1 = () => console.log("hello")
let test1 = (a, b) => a + b
箭頭后如果只有一個語句可以省略大括號篮洁,且如果是語句則語句的值作為函數(shù)返回值涩维,即可以省略 return
。如果有多個語句袁波,必須使用大括號瓦阐,且返回值一定要顯式通過 return
返回蜗侈。
let test1 = v => v + 1 // 函數(shù)返回 v + 1,不需要 return
let test2 = v => {
let a = v + 1
return a
}
調(diào)用
除了聲明時定義的形式參數(shù)睡蟋,每個函數(shù)還接收兩個附加參數(shù):this
和 arguments
宛篇。
當(dāng)實參與形參個數(shù)不匹配時不會導(dǎo)致運行時錯誤。如果實參過多薄湿,多出的參數(shù)被忽略叫倍。如果參數(shù)值過少,缺失的值將被替換為 undefined
豺瘤。對參數(shù)值不會進(jìn)行類型檢查吆倦。
函數(shù)內(nèi)的 this
函數(shù)內(nèi)可以訪問一個隱式的變量 this
,它的值取決于函數(shù)定義和調(diào)用的方式坐求。
如果函數(shù)不是通過箭頭定義的蚕泽,this
的值完全取決于調(diào)用方式。有四種:函數(shù)式桥嗤、方法式须妻、構(gòu)造式、apply泛领。
函數(shù)跟方法的區(qū)別即直接調(diào)用還是通過一個對象調(diào)用荒吏。當(dāng)函數(shù)被存儲為對象的一個屬性時,稱其為方法渊鞋。當(dāng)方法被調(diào)用時绰更,this
綁定到那個對象。
函數(shù)調(diào)用時锡宋,this
被綁定到全局對象儡湾。
let obj = {
a: 1,
good: function() { console.log(this && this.a) }
}
obj.good() // 方法,打印 1
let g = obj.good
g() // 函數(shù)执俩,打印 undefined
構(gòu)造式即通過 new
調(diào)用徐钠,此時函數(shù)是構(gòu)造器,this
指要創(chuàng)建的對象役首。
function Apple {
this.red = true
}
let apple = new Apple()
apply
和 call
函數(shù)的兩個特殊方法尝丐,可以顯式指定 this
的值。二者的區(qū)別是宋税,apply
需要實參放在一個數(shù)組中傳遞摊崭,而 call
的實參直接列出,見例子:
let obj = {
add: function(c, d) { this.a + this.b + this.c + this.d }
}
let add = obj.add
add.apply({a: 1, b: 2}, [3, 4])
add.call({a: 1, b: 2}, 3, 4)
ES5 有一個 bind
方法可以強(qiáng)制綁定函數(shù)的 this
到某個對象杰赛。此時呢簸,函數(shù)的調(diào)用不在受調(diào)用方式的影響。
let obj = {
a: 1,
b: 2,
add: function() { this.a + this.b }
}
let add = obj.add
add() // 報錯!
add = add.bind(obj)
add() // 正常
如果函數(shù)通過箭頭定義根时,則 this
綁定到函數(shù)定義時的上下文瘦赫。
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f))
}
}
某些情況下,如果必須訪問一個因為某種原因訪問不到的 this
蛤迎,一般先將這個 this
賦給另外一個變量(一般命名為 that
)确虱。然后傳遞或通過閉包訪問這個變量。
myObject.double = function () {
var that = this
var helper = function () {
that.value = add(that.value, that.value)
}
helper()
}
默認(rèn)參數(shù)(ES6)
function myfunction(value, option = 'default') {
}
myfunction(1)
myfunction(1, "my")
剩余參數(shù)和展開參數(shù)(ES6)
利用 ...
替裆,調(diào)用函數(shù)時校辩,將一個數(shù)組展開為多個參數(shù)。反過來辆童,收集剩余參數(shù)到一個數(shù)組宜咒。此特性用于替代 arguments
。
function f(x, ...y) {
// y is an Array
return x * y.length
}
f(3, "hello", true) === 6
function f(x, y, z) {
return x + y + z
}
f(...[1,2,3]) === 6
arguments
通過 arguments
數(shù)組可以訪問所有實參把鉴,包括那些多余參數(shù)故黑。這使得編寫一個無需指定參數(shù)個數(shù)的函數(shù)成為可能:
var sum = function () {
var i, sum = 0
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i]
}
return sum
};
document.writeln(sum(4, 8, 15, 16, 23, 42)) // 108
因為語言的設(shè)計錯誤,arguments
并不是一個真正的數(shù)組庭砍。它只是一個“類似數(shù)組”的對象场晶。arguments
有 length
屬性,但缺少所有的數(shù)組方法怠缸。
返回
函數(shù)總是會返回一個值诗轻。如果沒有指定返回值,返回 undefined
凯旭。
閉包
函數(shù)定義可以嵌套概耻。內(nèi)部函數(shù)可以訪問到外圍函數(shù)的參數(shù)和變量(this
和 arguments
除外)。通過函數(shù)字面量創(chuàng)建的函數(shù)對象包含到外部上下文的連接罐呼,這被稱為閉包。
通過閉包可以實現(xiàn)私有字段侦高、方法和公有字段嫉柴、方法的分隔。例如下面代碼奉呛,參數(shù) status
是私有的计螺,只能通過 getStatus
方法獲取:
var quo = function (status) {
return {
getStatus: function () {
return status
}
}
}
// Make an instance of quo.
var myQuo = quo("amazed")
document.writeln(myQuo.getStatus())
這個 que
函數(shù)被設(shè)計成無須在前面加上 new
來使用瞧壮,所以名字也沒有首字母大寫登馒。當(dāng)調(diào)用 que
時,返回一個新對象咆槽。該對象包含一個 getStatus
方法陈轿。即使 que
已經(jīng)返回了,但 getStatus
方法仍能訪問 que
對象的 status
參數(shù)。getStatus
方法并不訪問該參數(shù)的一個拷貝麦射,它訪問的是該參數(shù)本身蛾娶。函數(shù)可以訪問被創(chuàng)建時所處的上下文環(huán)境,這被稱為閉包潜秋。
理解內(nèi)部函數(shù)訪問外部函數(shù)的變量的實際值而非副本是很重要的蛔琅,見下面這個錯誤:
var add_the_handlers = function (nodes) {
var i
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i)
}
}
}
原本想每次彈出時顯示節(jié)點序號,但最后每個節(jié)點顯示的都是數(shù)字都等于節(jié)點數(shù)組峻呛。原因是事件處理函數(shù)綁定的是變量 i
罗售,而非 i
當(dāng)時的值。
正確的修改钩述,利用傳遞參數(shù)綁定當(dāng)時的值:
var add_the_handlers = function (nodes) {
var i
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (i) {
return function (e) {
alert(i)
}
}(i)
}
}
ES6 的 let
也可以解決該問題寨躁。每個 i
定義在自己的作用域中,是獨立的切距。
var add_the_handlers = function (nodes) {
for (let i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i)
}
}
}