W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
我們經(jīng)常需要在腳本的許多地方執(zhí)行很相似的操作。
例如,當(dāng)訪問者登錄、注銷或者在其他地方時(shí),我們需要顯示一條好看的信息。
函數(shù)是程序的主要“構(gòu)建模塊”。函數(shù)使該段代碼可以被調(diào)用很多次,而不需要寫重復(fù)的代碼。
我們已經(jīng)看到了內(nèi)建函數(shù)的示例,如 alert(message)
、prompt(message, default)
和 confirm(question)
。但我也可以創(chuàng)建自己的函數(shù)。
使用 函數(shù)聲明 創(chuàng)建函數(shù)。
看起來就像這樣:
function showMessage() {
alert( 'Hello everyone!' );
}
function
關(guān)鍵字首先出現(xiàn),然后是 函數(shù)名,然后是括號(hào)之間的 參數(shù) 列表(用逗號(hào)分隔,在上述示例中為空,我們將在接下來的示例中看到),最后是花括號(hào)之間的代碼(即“函數(shù)體”)。
function name(parameter1, parameter2, ... parameterN) {
...body...
}
我們的新函數(shù)可以通過名稱調(diào)用:showMessage()
。
例如:
function showMessage() {
alert( 'Hello everyone!' );
}
showMessage();
showMessage();
調(diào)用 showMessage()
執(zhí)行函數(shù)的代碼。這里我們會(huì)看到顯示兩次消息。
這個(gè)例子清楚地演示了函數(shù)的主要目的之一:避免代碼重復(fù)。
如果我們需要更改消息或其顯示方式,只需在一個(gè)地方修改代碼:輸出它的函數(shù)。
在函數(shù)中聲明的變量只在該函數(shù)內(nèi)部可見。
例如:
function showMessage() {
let message = "Hello, I'm JavaScript!"; // 局部變量
alert( message );
}
showMessage(); // Hello, I'm JavaScript!
alert( message ); // <-- 錯(cuò)誤!變量是函數(shù)的局部變量
函數(shù)也可以訪問外部變量,例如:
let userName = 'John';
function showMessage() {
let message = 'Hello, ' + userName;
alert(message);
}
showMessage(); // Hello, John
函數(shù)對外部變量擁有全部的訪問權(quán)限。函數(shù)也可以修改外部變量。
例如:
let userName = 'John';
function showMessage() {
userName = "Bob"; // (1) 改變外部變量
let message = 'Hello, ' + userName;
alert(message);
}
alert( userName ); // John 在函數(shù)調(diào)用之前
showMessage();
alert( userName ); // Bob,值被函數(shù)修改了
只有在沒有局部變量的情況下才會(huì)使用外部變量。
如果在函數(shù)內(nèi)部聲明了同名變量,那么函數(shù)會(huì) 遮蔽 外部變量。例如,在下面的代碼中,函數(shù)使用局部的 userName
,而外部變量被忽略:
let userName = 'John';
function showMessage() {
let userName = "Bob"; // 聲明一個(gè)局部變量
let message = 'Hello, ' + userName; // Bob
alert(message);
}
// 函數(shù)會(huì)創(chuàng)建并使用它自己的 userName
showMessage();
alert( userName ); // John,未被更改,函數(shù)沒有訪問外部變量。
全局變量
任何函數(shù)之外聲明的變量,例如上述代碼中的外部變量
userName
,都被稱為 全局 變量。
全局變量在任意函數(shù)中都是可見的(除非被局部變量遮蔽)。
減少全局變量的使用是一種很好的做法?,F(xiàn)代的代碼有很少甚至沒有全局變量。大多數(shù)變量存在于它們的函數(shù)中。但是有時(shí)候,全局變量能夠用于存儲(chǔ)項(xiàng)目級別的數(shù)據(jù)。
我們可以通過參數(shù)將任意數(shù)據(jù)傳遞給函數(shù)。
在如下示例中,函數(shù)有兩個(gè)參數(shù):from
和 text
。
function showMessage(from, text) { // 參數(shù):from 和 text
alert(from + ': ' + text);
}
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
當(dāng)函數(shù)在 (*)
和 (**)
行中被調(diào)用時(shí),給定值被復(fù)制到了局部變量 from
和 text
。然后函數(shù)使用它們進(jìn)行計(jì)算。
這里還有一個(gè)例子:我們有一個(gè)變量 from
,并將它傳遞給函數(shù)。請注意:函數(shù)會(huì)修改 from
,但在函數(shù)外部看不到更改,因?yàn)楹瘮?shù)修改的是復(fù)制的變量值副本:
function showMessage(from, text) {
from = '*' + from + '*'; // 讓 "from" 看起來更優(yōu)雅
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello"); // *Ann*: Hello
// "from" 值相同,函數(shù)修改了一個(gè)局部的副本。
alert( from ); // Ann
當(dāng)一個(gè)值被作為函數(shù)參數(shù)(parameter)傳遞時(shí),它也被稱為 參數(shù)(argument)。
換一種方式,我們把這些術(shù)語搞清楚:
我們聲明函數(shù)時(shí)列出它們的參數(shù)(parameters),然后調(diào)用它們傳遞參數(shù)(arguments)。
在上面的例子中,我們可以說:“函數(shù) showMessage
被聲明,并且?guī)в袃蓚€(gè)參數(shù)(parameters),隨后它被調(diào)用,兩個(gè)參數(shù)(arguments)分別為 from
和 "Hello"
”。
如果一個(gè)函數(shù)被調(diào)用,但有參數(shù)(argument)未被提供,那么相應(yīng)的值就會(huì)變成 undefined
。
例如,之前提到的函數(shù) showMessage(from, text)
可以只使用一個(gè)參數(shù)(argument)調(diào)用:
showMessage("Ann");
那不是錯(cuò)誤,這樣調(diào)用將輸出 "*Ann*: undefined"
。因?yàn)閰?shù) text
的值未被傳遞,所以變成了 undefined
。
我們可以使用 =
為函數(shù)聲明中的參數(shù)指定所謂的“默認(rèn)”(如果對應(yīng)參數(shù)的值未被傳遞則使用)值:
function showMessage(from, text = "no text given") {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text given
現(xiàn)在如果 text
參數(shù)未被傳遞,它將會(huì)得到值 "no text given"
。
這里 "no text given"
是一個(gè)字符串,但它可以是更復(fù)雜的表達(dá)式,并且只會(huì)在缺少參數(shù)時(shí)才會(huì)被計(jì)算和分配。所以,這也是可能的:
function showMessage(from, text = anotherFunction()) {
// anotherFunction() 僅在沒有給定 text 時(shí)執(zhí)行
// 其運(yùn)行結(jié)果將成為 text 的值
}
默認(rèn)參數(shù)的計(jì)算
在 JavaScript 中,每次函數(shù)在沒帶個(gè)別參數(shù)的情況下被調(diào)用,默認(rèn)參數(shù)會(huì)被計(jì)算出來。
在上面的例子中,如果傳遞了參數(shù)
text
,那么anotherFunction()
就不會(huì)被調(diào)用。
如果沒傳遞參數(shù)
text
,那么anotherFunction()
就會(huì)被調(diào)用。
在 JavaScript 老代碼中的默認(rèn)參數(shù)
幾年前,JavaScript 不支持默認(rèn)參數(shù)的語法。所以人們使用其他方式來設(shè)置默認(rèn)參數(shù)。
如今,我們會(huì)在舊代碼中看到它們。
例如,顯式地檢查
undefined
:
function showMessage(from, text) { if (text === undefined) { text = 'no text given'; } alert( from + ": " + text ); }
……或者使用
||
運(yùn)算符:
function showMessage(from, text) { // 如果 text 的值為假值,則分配默認(rèn)值 // 這樣賦值 text == "" 與 text 無值相同 text = text || 'no text given'; ... }
有些時(shí)候,將參數(shù)默認(rèn)值的設(shè)置放在函數(shù)執(zhí)行(相較更后期)而不是函數(shù)聲明時(shí),也行得通。
我們可以通過將參數(shù)與 undefined
進(jìn)行比較,來檢查該參數(shù)是否在函數(shù)執(zhí)行期間被傳遞進(jìn)來:
function showMessage(text) {
// ...
if (text === undefined) { // 如果參數(shù)未被傳遞進(jìn)來
text = 'empty message';
}
alert(text);
}
showMessage(); // empty message
……或者我們可以使用 ||
運(yùn)算符:
function showMessage(text) {
// 如果 text 為 undefined 或者為假值,那么將其賦值為 'empty'
text = text || 'empty';
...
}
現(xiàn)代 JavaScript 引擎支持 空值合并運(yùn)算符 ??
,它在大多數(shù)假值(例如 0
)應(yīng)該被視為“正常值”時(shí)更具優(yōu)勢:
function showCount(count) {
// 如果 count 為 undefined 或 null,則提示 "unknown"
alert(count ?? "unknown");
}
showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown
函數(shù)可以將一個(gè)值返回到調(diào)用代碼中作為結(jié)果。
最簡單的例子是將兩個(gè)值相加的函數(shù):
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
指令 return
可以在函數(shù)的任意位置。當(dāng)執(zhí)行到達(dá)時(shí),函數(shù)停止,并將值返回給調(diào)用代碼(分配給上述代碼中的 result
)。
在一個(gè)函數(shù)中可能會(huì)出現(xiàn)很多次 return
。例如:
function checkAge(age) {
if (age >= 18) {
return true;
} else {
return confirm('Got a permission from the parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Access denied' );
}
只使用 return
但沒有返回值也是可行的。但這會(huì)導(dǎo)致函數(shù)立即退出。
例如:
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert( "Showing you the movie" ); // (*)
// ...
}
在上述代碼中,如果 checkAge(age)
返回 false
,那么 showMovie
將不會(huì)運(yùn)行到 alert
。
空值的 ?
return
?或沒有 ?return
?的函數(shù)返回值為 ?undefined
?如果函數(shù)無返回值,它就會(huì)像返回
undefined
一樣:
function doNothing() { /* 沒有代碼 */ } alert( doNothing() === undefined ); // true
空值的
return
和return undefined
等效:
function doNothing() { return; } alert( doNothing() === undefined ); // true
不要在 ?
return
?與返回值之間添加新行對于
return
的長表達(dá)式,可能你會(huì)很想將其放在單獨(dú)一行,如下所示:
return (some + long + expression + or + whatever * f(a) + f(b))
但這不行,因?yàn)?JavaScript 默認(rèn)會(huì)在
return
之后加上分號(hào)。上面這段代碼和下面這段代碼運(yùn)行流程相同:
return; (some + long + expression + or + whatever * f(a) + f(b))
因此,實(shí)際上它的返回值變成了空值。
如果我們想要將返回的表達(dá)式寫成跨多行的形式,那么應(yīng)該在
return
的同一行開始寫此表達(dá)式。或者至少按照如下的方式放上左括號(hào):
return ( some + long + expression + or + whatever * f(a) + f(b) )
然后它就能像我們預(yù)想的那樣正常運(yùn)行了。
函數(shù)就是行為(action)。所以它們的名字通常是動(dòng)詞。它應(yīng)該簡短且盡可能準(zhǔn)確地描述函數(shù)的作用。這樣讀代碼的人就能清楚地知道這個(gè)函數(shù)的功能。
一種普遍的做法是用動(dòng)詞前綴來開始一個(gè)函數(shù),這個(gè)前綴模糊地描述了這個(gè)行為。團(tuán)隊(duì)內(nèi)部必須就前綴的含義達(dá)成一致。
例如,以 "show"
開頭的函數(shù)通常會(huì)顯示某些內(nèi)容。
函數(shù)以 XX 開始……
"get…"
? —— 返回一個(gè)值,"calc…"
? —— 計(jì)算某些內(nèi)容,"create…"
? —— 創(chuàng)建某些內(nèi)容,"check…"
? —— 檢查某些內(nèi)容并返回 boolean 值,等。這類名字的示例:
showMessage(..) // 顯示信息
getAge(..) // 返回 age(gets it somehow)
calcSum(..) // 計(jì)算求和并返回結(jié)果
createForm(..) // 創(chuàng)建表單(通常會(huì)返回它)
checkPermission(..) // 檢查權(quán)限并返回 true/false
有了前綴,只需瞥一眼函數(shù)名,就可以了解它的功能是什么,返回什么樣的值。
一個(gè)函數(shù) —— 一個(gè)行為
一個(gè)函數(shù)應(yīng)該只包含函數(shù)名所指定的功能,而不是做更多與函數(shù)名無關(guān)的功能。
兩個(gè)獨(dú)立的行為通常需要兩個(gè)函數(shù),即使它們通常被一起調(diào)用(在這種情況下,我們可以創(chuàng)建第三個(gè)函數(shù)來調(diào)用這兩個(gè)函數(shù))。
有幾個(gè)違反這一規(guī)則的例子:
- ?
getAge
?—— 如果它通過 ?alert
?將 age 顯示出來,那就有問題了(只應(yīng)該是獲?。?。- ?
createForm
?—— 如果它包含修改文檔的操作,例如向文檔添加一個(gè)表單,那就有問題了(只應(yīng)該創(chuàng)建表單并返回)。- ?
checkPermission
?—— 如果它顯示 ?access granted/denied
? 消息,那就有問題了(只應(yīng)執(zhí)行檢查并返回結(jié)果)。這些例子假設(shè)函數(shù)名前綴具有通用的含義。你和你的團(tuán)隊(duì)可以自定義這些函數(shù)名前綴的含義,但是通常都沒有太大的不同。無論怎樣,你都應(yīng)該對函數(shù)名前綴的含義、帶特定前綴的函數(shù)可以做什么以及不可以做什么有深刻的了解。所有相同前綴的函數(shù)都應(yīng)該遵守相同的規(guī)則。并且,團(tuán)隊(duì)成員應(yīng)該形成共識(shí)。
非常短的函數(shù)命名
常用的函數(shù)有時(shí)會(huì)有非常短的名字。
例如,jQuery 框架用
$
定義一個(gè)函數(shù)。LoDash 庫的核心函數(shù)用_
命名。
這些都是例外,一般而言,函數(shù)名應(yīng)簡明扼要且具有描述性。
函數(shù)應(yīng)該簡短且只有一個(gè)功能。如果這個(gè)函數(shù)功能復(fù)雜,那么把該函數(shù)拆分成幾個(gè)小的函數(shù)是值得的。有時(shí)候遵循這個(gè)規(guī)則并不是那么容易,但這絕對是件好事。
一個(gè)單獨(dú)的函數(shù)不僅更容易測試和調(diào)試 —— 它的存在本身就是一個(gè)很好的注釋!
例如,比較如下兩個(gè)函數(shù) showPrimes(n)
。它們的功能都是輸出到 n
的 素?cái)?shù)。
第一個(gè)變體使用了一個(gè)標(biāo)簽:
function showPrimes(n) {
nextPrime: for (let i = 2; i < n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
alert( i ); // 一個(gè)素?cái)?shù)
}
}
第二個(gè)變體使用附加函數(shù) isPrime(n)
來檢驗(yàn)素?cái)?shù):
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
alert(i); // 一個(gè)素?cái)?shù)
}
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
第二個(gè)變體更容易理解,不是嗎?我們通過函數(shù)名(isPrime
)就可以看出函數(shù)的行為,而不需要通過代碼。人們通常把這樣的代碼稱為 自描述。
因此,即使我們不打算重用它們,也可以創(chuàng)建函數(shù)。函數(shù)可以讓代碼結(jié)構(gòu)更清晰,可讀性更強(qiáng)。
函數(shù)聲明方式如下所示:
function name(parameters, delimited, by, comma) {
/* code */
}
undefined
?。為了使代碼簡潔易懂,建議在函數(shù)中主要使用局部變量和參數(shù),而不是外部變量。
與不獲取參數(shù)但將修改外部變量作為副作用的函數(shù)相比,獲取參數(shù)、使用參數(shù)并返回結(jié)果的函數(shù)更容易理解。
函數(shù)命名:
create…
?、?show…
?、?get…
?、?check…
? 等等。使用它們來提示函數(shù)的作用吧。函數(shù)是腳本的主要構(gòu)建塊?,F(xiàn)在我們已經(jīng)介紹了基本知識(shí),現(xiàn)在我們就可以開始創(chuàng)建和使用函數(shù)了。但這只是學(xué)習(xí)和使用函數(shù)的開始。我們將繼續(xù)學(xué)習(xí)更多函數(shù)的相關(guān)知識(shí),更深入地研究它們的先進(jìn)特征。
重要程度: 4
如果參數(shù) age
大于 18
,那么下面的函數(shù)將返回 true
。
否則它將會(huì)要求進(jìn)行確認(rèn),并返回確認(rèn)結(jié)果:
function checkAge(age) {
if (age > 18) {
return true;
} else {
// ...
return confirm('父母允許嗎?');
}
}
如果 else
被刪除,函數(shù)的工作方式會(huì)不同嗎?
function checkAge(age) {
if (age > 18) {
return true;
}
// ...
return confirm('父母允許嗎?');
}
這兩個(gè)變體的行為是否有區(qū)別?
沒有區(qū)別。
在這兩種情況下,return confirm('父母允許嗎?')
均只會(huì)在 if
條件為假時(shí)執(zhí)行。
重要程度: 4
如果參數(shù) age
大于 18
,那么下面的函數(shù)返回 true
。
否則它將會(huì)要求進(jìn)行確認(rèn),并返回確認(rèn)結(jié)果:
function checkAge(age) {
if (age > 18) {
return true;
} else {
return confirm('Do you have your parents permission to access this page?');
}
}
重寫這個(gè)函數(shù)并保證效果相同,不使用 if
,且只需一行代碼。
編寫 ?checkAge
?的兩個(gè)變體:
?
?||
?使用問號(hào)運(yùn)算符 '?'
:
function checkAge(age) {
return (age > 18) ? true : confirm('Did parents allow you?');
}
使用或運(yùn)算符 ||
(最短的變體):
function checkAge(age) {
return (age > 18) || confirm('Did parents allow you?');
}
請注意此處不需要 age > 18
左右的括號(hào)。寫上括號(hào)是為了提高可讀性。
重要程度: 1
寫一個(gè)返回?cái)?shù)字 a
和 b
中較小的那個(gè)數(shù)字的函數(shù) min(a,b)
。
例如:
min(2, 5) == 2
min(3, -1) == -1
min(1, 1) == 1
使用 if
的解決方案:
function min(a, b) {
if (a < b) {
return a;
} else {
return b;
}
}
使用問號(hào)運(yùn)算符 '?'
的解決方案:
function min(a, b) {
return a < b ? a : b;
}
P.S. 在 a == b
的情況下,返回什么都無關(guān)緊要。
重要程度: 4
寫一個(gè)函數(shù) pow(x,n)
,返回 x
的 n
次方。換句話說,將 x
與自身相乘 n
次,返回最終結(jié)果。
pow(3, 2) = 3 * 3 = 9
pow(3, 3) = 3 * 3 * 3 = 27
pow(1, 100) = 1 * 1 * ...*1 = 1
創(chuàng)建一個(gè) web 頁面,提示輸入 x
和 n
,然后返回 pow(x,n)
的運(yùn)算結(jié)果。
P.S. 在這個(gè)任務(wù)中,函數(shù)應(yīng)該只支持自然數(shù) n
:從 1
開始的整數(shù)。
function pow(x, n) {
let result = x;
for (let i = 1; i < n; i++) {
result *= x;
}
return result;
}
let x = prompt("x?", '');
let n = prompt("n?", '');
if (n < 1) {
alert(`Power ${n} is not supported, use a positive integer`);
} else {
alert( pow(x, n) );
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: