盤點(diǎn)JavaScript函數(shù)的基本知識(shí)
本文旨在提供web開發(fā)人員必須了解的所有JavaScript函數(shù)的基本知識(shí)。
函數(shù)于軟件開發(fā)者而言并不是什么奇幻世界。如果你的日?;顒?dòng)涉及到編碼,哪怕是一點(diǎn)點(diǎn),那么在一天結(jié)束的時(shí)候,你一定創(chuàng)建/修改了一個(gè)或多個(gè)函數(shù)。
簡(jiǎn)而言之函數(shù)只不過(guò)是一組執(zhí)行某個(gè)操作的語(yǔ)句。函數(shù)可能會(huì)有一些輸入?yún)?shù)(在函數(shù)體中使用),并在執(zhí)行后返回值。
JavaScript函數(shù)也具有這些特性,但它們不僅僅是常規(guī)函數(shù)。JavaScript函數(shù)是對(duì)象。你可以查看我曾經(jīng)寫的關(guān)于JavaScript對(duì)象的文章,里面我提到幾乎JavaScript中的所有一切都是對(duì)象。
作為對(duì)象,JavaScript函數(shù)可能會(huì)有屬性和其他函數(shù)(方法)。讓我們來(lái)看看JavaScript中的一個(gè)典型的函數(shù)定義。
function myNotSoGreatFunc(visitor) {
console.log("Welcome to Code Morning Mr. " + visitor);
}
沒(méi)錯(cuò)。上面的函數(shù)不涉及什么宏偉大業(yè),因?yàn)樗鼉H是對(duì)博客訪問(wèn)者表示了歡迎。但它展示了JavaScript函數(shù)的樣子。函數(shù)定義從關(guān)鍵字function
開始,然后是函數(shù)名,空的或有參數(shù)的括號(hào)。實(shí)際的函數(shù)代碼(JavaScript語(yǔ)句)被封裝在一對(duì)花括號(hào)內(nèi){ }。對(duì)于函數(shù)而言,return
語(yǔ)句是可選的。JavaScript函數(shù)總是會(huì)返回一個(gè)值。當(dāng)function
主體中沒(méi)有return
語(yǔ)句時(shí),那么function
返回undefined。
下面的代碼調(diào)用傳遞visitor name作為參數(shù)的函數(shù)。
myNotSoGreatFunc("Bob Martin");
// Output:
// Welcome to Code Morning Mr. Bob Martin.
到現(xiàn)在為止,我們了解了函數(shù)非?;镜奶卣鳌,F(xiàn)在,我們將對(duì)JavaScript函數(shù)的一些高級(jí)概念一探究竟。
匿名函數(shù)
JavaScript函數(shù)可以是匿名的。這意味著你可以從函數(shù)聲明中省略函數(shù)名。但是,函數(shù)必須存儲(chǔ)在變量中。
var addNumbers = function (x, y) { return x + y; }
上述語(yǔ)法被也被稱為函數(shù)表達(dá)式。你可以把變量addNumbers
當(dāng)作函數(shù)名,以及像下面這樣調(diào)用該函數(shù)。
var sum = addNumbers(2, 3);
當(dāng)你想傳遞一個(gè)函數(shù)作為參數(shù)給另一個(gè)函數(shù)時(shí),函數(shù)表達(dá)式就非常方便了。讓我們用一個(gè)簡(jiǎn)單的例子來(lái)試著了解這一點(diǎn)。
var add = function (first, second) { return first + second };
var multiply = function (first, second) { return first * second };
function calculate(fun, a, b) {
return fun(a, b);
}
首先我已經(jīng)創(chuàng)建了兩個(gè)匿名函數(shù)。第一個(gè)返回兩個(gè)數(shù)的加法運(yùn)算,第二個(gè)返回兩個(gè)數(shù)的乘法運(yùn)算。相當(dāng)簡(jiǎn)單,沒(méi)有什么可值得炫耀的地方。然后,我定義函數(shù)calculate
,這個(gè)函數(shù)接受函數(shù)作為第一個(gè)參數(shù)后跟兩個(gè)參數(shù)接受兩個(gè)數(shù)字。
我可以通過(guò)傳遞任意函數(shù)作為第一個(gè)參數(shù)來(lái)調(diào)用函數(shù)calculate。
var sum = calculate(add, 2, 3); // sum = 5
var multiplication = calculate(multiply, 2, 3); // multiplication = 6
你可以看到將函數(shù)作為參數(shù)傳遞是多么容易。這種模式在AJAX中大量使用,當(dāng)你在AJAX調(diào)用完成后,傳遞回調(diào)函數(shù)處理成功或失敗的場(chǎng)景時(shí)。
關(guān)于參數(shù)的更多內(nèi)容
JavaScript是非常靈活的,當(dāng)涉及到傳遞或訪問(wèn)函數(shù)參數(shù)的時(shí)候。讓我們看一下函數(shù)參數(shù)可以被操縱的方式。
缺少參數(shù)
調(diào)用函數(shù)時(shí),函數(shù)的參數(shù)數(shù)量可以比要求的更少或更多。如果你調(diào)用的函數(shù)的參數(shù)比聲明的少,那么缺少的參數(shù)被設(shè)置為undefined。
function callMe(a, b, c) {
console.log("c is " + typeof c);
}
callMe("Code", "Morning");
// Output: "c is undefined"
callMe("Learn", "JavaScript", "Functions");
// Output: "c is string"
Arguments對(duì)象
所有的JavaScript函數(shù)有一個(gè)特殊的對(duì)象,叫做arguments
,它是在函數(shù)調(diào)用過(guò)程中傳遞的參數(shù)數(shù)組。該對(duì)象可以被用來(lái)訪問(wèn)單個(gè)參數(shù)或獲得傳遞到函數(shù)的參數(shù)總數(shù)。
function callMe() {
var i;
for (i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
console.log("Total arguments passed: " + arguments.length);
}
此函數(shù)假設(shè)沒(méi)有傳遞任何參數(shù),但就像我說(shuō)的,你可以傳遞任何數(shù)量的參數(shù)到JavaScript函數(shù)。我可以像這樣調(diào)用這個(gè)函數(shù):
callMe("Code", "Morning", "Mr. Programmer");
// Output":
// Code
// Morning
// Mr. Programmer
// Total arguments passed: 3
每個(gè)參數(shù)可以從arguments
對(duì)象作為一個(gè)數(shù)組項(xiàng)被訪問(wèn)。被傳遞給函數(shù)的arguments
的總數(shù)可從arguments.length屬性獲得。
默認(rèn)參數(shù)
你是C ++或C#程序員嗎?你見(jiàn)過(guò)使用默認(rèn)參數(shù)的函數(shù)嗎?也許你會(huì)回答yes! ECMAScript 6帶來(lái)了JavaScript的這一特性,就是你可以定義帶有默認(rèn)參數(shù)的函數(shù)。
function greetMyVisitors(name, profession = "The cool programmer") {
alert("Welcome Mr. " + name + ", " + profession);
}
該函數(shù)有禮貌地地迎接了博客訪問(wèn)者。它有兩個(gè)參數(shù)name
和profession
,并在消息框中顯示一個(gè)歡迎消息。如果在調(diào)用過(guò)程中沒(méi)有參數(shù)(或“undefined”)傳遞,那么第二個(gè)參數(shù)取用默認(rèn)值。
greetMyVisitors("Justin Bieber", "The singer");
// Shows the message "Welcome Mr. Justin Bieber, The singer"
greetMyVisitors("Bob Martin");
// Shows the message "Welcome Mr. Bob Martin, The cool programmer"
greetMyVisitors("John Papa", undefined);
// Shows the message "Welcome Mr. John Papa, The cool programmer"
嵌套函數(shù)
函數(shù)可以在它的內(nèi)部包含一個(gè)或多個(gè)函數(shù)。內(nèi)部函數(shù)可能會(huì)在內(nèi)部再次包含函數(shù)。讓我們來(lái)看看以下操作。
function wakeUpAndCode() {
function wakeUp() {
console.log("I just woke up");
}
function code() {
console.log("I am ready to code now");
}
wakeUp();
code();
}
wakeUpAndCode();
// Output:
// I just woke up
// I am ready to code now
函數(shù)wakeUpAndCode
包含兩個(gè)內(nèi)部函數(shù)wakeUp
和code。當(dāng)調(diào)用wakeUpAndCode時(shí),函數(shù)主體開始執(zhí)行函數(shù)主體。在外部函數(shù)中只有兩個(gè)可執(zhí)行語(yǔ)句,調(diào)用wakeUp
和code
的方法。調(diào)用wakeUp
將執(zhí)行內(nèi)部wakeUp
函數(shù),這將寫入string
“I just woke up”到控制臺(tái)。調(diào)用code
將會(huì)寫入“I am ready to code now”string
到控制臺(tái)。
內(nèi)部函數(shù)可以訪問(wèn)所有外部函數(shù)的變量和參數(shù)。內(nèi)部函數(shù)是函數(shù)內(nèi)部某種private
實(shí)現(xiàn),并且不能從外部函數(shù)以外被調(diào)用。內(nèi)部函數(shù)的使用生成了JavaScript閉包,這個(gè)我將另起一篇文章討論。
立即執(zhí)行函數(shù)表達(dá)式(IIFE,發(fā)音iffy)
IIFE是被立即調(diào)用執(zhí)行的匿名函數(shù)表達(dá)式。IIFE看上去像這樣:
(function() {
// Your awesome code here
}());
所有你要做的就是創(chuàng)建一個(gè)匿名函數(shù),在函數(shù)定義后馬上放一對(duì)圓括號(hào)以調(diào)用函數(shù),最后將所有代碼封裝在另一對(duì)圓括號(hào)中。最外層的括號(hào)將它里面的所有一切轉(zhuǎn)變成一個(gè)表達(dá)式,因?yàn)槔ㄌ?hào)不能包含JavaScript語(yǔ)句。函數(shù)定義后面的圓括號(hào)則立即調(diào)用函數(shù)。
IIFE塊中定義的任何變量或函數(shù)對(duì)塊而言是本地的,并且不能被這個(gè)范圍以外的任何代碼改變。
看看IIFE的這個(gè)例子。此函數(shù)沒(méi)有調(diào)用也會(huì)自動(dòng)執(zhí)行。
(function() {
console.log("I run on my own.");
}());
只需在plunker中復(fù)制并粘貼代碼,看看在瀏覽器控制臺(tái)中的輸出。如果你不知道去哪里找瀏覽器控制臺(tái),那么只要在瀏覽器窗口中按下F12就會(huì)出現(xiàn)開發(fā)者工具。跳轉(zhuǎn)console選項(xiàng)卡以查看console.log語(yǔ)句的所有輸出。
IIFE是一個(gè)在代碼中創(chuàng)建局部范圍的很好方法。它們可以幫助你保護(hù)變量和函數(shù),以避免被應(yīng)用程序的其他部分更改或覆蓋。JavaScript中IIFE的其他優(yōu)勢(shì)?它們是如何解決全局范圍污染問(wèn)題的?歡迎點(diǎn)擊查看我關(guān)于立即執(zhí)行函數(shù)表達(dá)式的文章。
構(gòu)造函數(shù)
函數(shù)可以充當(dāng)構(gòu)造器的角色,并且可以使用構(gòu)造函數(shù)來(lái)創(chuàng)建新的對(duì)象。這是使JavaScript面向?qū)ο蟮奶攸c(diǎn)之一。使用構(gòu)造函數(shù)的好處是,你將能夠通過(guò)預(yù)定義的屬性和方法,創(chuàng)造盡可能多的對(duì)象。如果你由此關(guān)聯(lián)到其他語(yǔ)言中的類和對(duì)象,那么你做的對(duì)。
讓我們創(chuàng)建一個(gè)帶有一些屬性和方法的構(gòu)造函數(shù)Programmer
。你可以假設(shè)它在你最喜歡的語(yǔ)言中是一個(gè)類。
function Programmer(name, company, expertise) {
this.name = name;
this.company = company;
this.expertise = expertise;
this.writeCode = function() {
console.log("Writing some public static thing..");
}
this.makeSkypeCall = function() {
console.log("Making skype call..");
}
this.doSalsa = function() {
console.log("I'm a programmer, I can only do Gangnam style..");
}
this.canWriteJavaScript = function() {
return expertise === "JavaScript";
}
}
函數(shù)有三個(gè)參數(shù),并創(chuàng)建了一個(gè)具有三個(gè)屬性和四種方法的對(duì)象。我不認(rèn)為上面的代碼需要任何解釋。此外,我可以創(chuàng)建任意數(shù)量程序員對(duì)象。
var javaProgrammer = new Programmer("Mohit Srivastava", "Infosys", "Java");
var dotnetProgrammer = new Programmer("Atul Mishra", "Prowareness", ".NET");
雖然也可以創(chuàng)建一個(gè)使用對(duì)象文本語(yǔ)法帶有相同屬性和方法的對(duì)象,但我們需要多次編寫相同的代碼,這可不是什么偉大的實(shí)踐。如果你知道編程DRY原則,那么你就不會(huì)不贊同我。構(gòu)造函數(shù)使得可以一次定義對(duì)象,并創(chuàng)建真正的實(shí)例,無(wú)論什么時(shí)候你想要。
警告!
始終使用new關(guān)鍵字來(lái)從構(gòu)造器創(chuàng)建新的對(duì)象。忘記了new
而像這個(gè)創(chuàng)建一個(gè)實(shí)例->
var jsProgrammer = Programmer("Douglas Crockford", "Yahoo", "JavaScript")
最終將添加所有屬性和方法到全局的window
對(duì)象,哇哦,這將是太可怕了。原因是,除非明確指定,否則“this”指向全局的window
對(duì)象。使用new
設(shè)置“this”上下文到被創(chuàng)建的當(dāng)前對(duì)象。
然而,有一種變通方法可以來(lái)克服這個(gè)問(wèn)題。你可以改變構(gòu)造函數(shù)的實(shí)現(xiàn)以使域安全,然后在創(chuàng)建新的對(duì)象時(shí),你就可以愉快地忽略new
關(guān)鍵字了。請(qǐng)參見(jiàn)以下修改了的構(gòu)造函數(shù)代碼。為了便于查看,我已刪除了一些方法。
function Programmer(name, company, expertise) {
if(!(this instanceof Programmer)) {
return new Programmer(name, company, expertise);
}
this.name = name;
this.company = company;
this.expertise = expertise;
this.writeCode = function() {
console.log("Writing some public static thing..");
}
}
if
條件檢查了this
對(duì)象是否是Programmer的一個(gè)實(shí)例。如果不是,它會(huì)創(chuàng)建一個(gè)新的Programmer
對(duì)象,并通過(guò)再次調(diào)用構(gòu)造器返回相同的內(nèi)容。
注意:你無(wú)法在不使用’new’關(guān)鍵字的情況下,在Strict
模式下從構(gòu)造器創(chuàng)建一個(gè)新的對(duì)象。Strict
模式強(qiáng)制一些編碼準(zhǔn)則,并且在你寫的東西不安全的情況下會(huì)拋出錯(cuò)誤。要啟用Strict
模式,你只需要添加在你的代碼開頭添加字符串 ‘use strict
’。在Strict模式下運(yùn)行代碼是一個(gè)良好的實(shí)踐。
'use strict'
function doSomething() { ... }
....
....
在這篇文章中,我?guī)缀跻呀?jīng)涵蓋了有關(guān)函數(shù)的所有內(nèi)容。函數(shù)被認(rèn)為是JavaScript中的一等公民。理解函數(shù)可能是最重要的事情,如果你想掌握J(rèn)avaScript的話。
歡迎各位指正。