深入理解 JavaScript 中的函數

字號+ 編輯: 种花家 修訂: 种花家 來源: 腾讯QQ兴趣部落 2023-09-12 我要說兩句(0)

這篇文章幾乎已經涵蓋了有關函數的所有内容。函數被認爲是JavaScript中的一等公民。理解函數可能是最重要的事情,如果你想掌握JavaScript的話。

函數於軟體開發者而言並不是什麽奇幻世界。如果你的日常活動涉及到編碼,哪怕是一點點,那麽在一天結束的時候,你一定創建/修改了一個或多個函數。

簡而言之函數隻不過是一組執行某個操作的語句。函數可能會有一些輸入參數(在函數體中使用),並在執行後返回值。

JavaScript函數也具有這些特性,但它們不僅僅是常槼函數。JavaScript函數是對象。你可以查看我曾經寫的關於JavaScript對象的文章,裡面我提到幾乎JavaScript中的所有一切都是對象。

作爲對象,JavaScript函數可能會有屬性和其他函數(方法)。讓我們來看看JavaScript中的一個典型的函數定義。

function myNotSoGreatFunc(visitor) {
    console.log("Welcome to Code Morning Mr. " + visitor);
}

沒錯。上面的函數不涉及什麽宏偉大業,因爲它僅是對部落格訪問者表示了歡迎。但它展示了JavaScript函數的樣子。函數定義從關鍵字function開始,然後是函數名,空的或有參數的括號。實際的函數代碼(JavaScript語句)被封裝在一對花括號内{ }。對於函數而言,return語句是可選的。JavaScript函數總是會返回一個值。當function主體中沒有return語句時,那麽function返回undefined。

下面的代碼調用傳遞visitor name作爲參數的函數。

myNotSoGreatFunc("Bob Martin");
// Output:
// Welcome to Code Morning Mr. Bob Martin.

到現在爲止,我們了解了函數非常基本的特征。現在,我們將對JavaScript函數的一些高級概念一探究竟。

匿名函數

JavaScript函數可以是匿名的。這意味著你可以從函數聲明中省略函數名。但是,函數必須存儲在變量中。

var addNumbers = function (x, y) {
    return x + y;
}

上述語法被也被稱爲函數表達式。你可以把變量addNumbers 當作函數名,以及像下面這樣調用該函數。

var sum = addNumbers(2, 3);

當你想傳遞一個函數作爲參數給另一個函數時,函數表達式就非常方便了。讓我們用一個簡單的例子來試著了解這一點。

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);
}

首先我已經創建了兩個匿名函數。第一個返回兩個數的加法運算,第二個返回兩個數的乘法運算。相當簡單,沒有什麽可值得炫耀的地方。然後,我定義函數calculate,這個函數接受函數作爲第一個參數後跟兩個參數接受兩個數字。

我可以通過傳遞任意函數作爲第一個參數來調用函數calculate。

var sum = calculate(add, 2, 3); // sum = 5
var multiplication = calculate(multiply, 2, 3); // multiplication = 6

你可以看到將函數作爲參數傳遞是多麽容易。這種模式在AJAX中大量使用,當你在AJAX調用完成後,傳遞回調函數處理成功或失敗的場景時。

關於參數的更多内容

JavaScript是非常靈活的,當涉及到傳遞或訪問函數參數的時候。讓我們看一下函數參數可以被操縱的方式。

缺少參數

調用函數時,函數的參數數量可以比要求的更少或更多。如果你調用的函數的參數比聲明的少,那麽缺少的參數被設置爲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對象

所有的JavaScript函數有一個特殊的對象,叫做arguments,它是在函數調用過程中傳遞的參數數組。該對象可以被用來訪問單個參數或獲得傳遞到函數的參數總數。

function callMe() {
    var i;
    for (i = 0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
    console.log("Total arguments passed: " + arguments.length);
}

此函數假設沒有傳遞任何參數,但就像我說的,你可以傳遞任何數量的參數到JavaScript函數。我可以像這樣調用這個函數:

callMe("Code", "Morning", "Mr. Programmer");
// Output":
// Code
// Morning
// Mr. Programmer
// Total arguments passed: 3

每個參數可以從arguments對象作爲一個數組項被訪問。被傳遞給函數的arguments的總數可從arguments.length屬性獲得。

默認參數

你是C ++或C#程序員嗎?你見過使用默認參數的函數嗎?也許你會回答yes! ECMAScript 6帶來了JavaScript的這一特性,就是你可以定義帶有默認參數的函數。

function greetMyVisitors(name, profession = "The cool programmer") {
    alert("Welcome Mr. " + name + ", " + profession);
}

該函數有禮貌地地迎接了部落格訪問者。它有兩個參數name 和profession,並在消息框中顯示一個歡迎消息。如果在調用過程中沒有參數(或“undefined”)傳遞,那麽第二個參數取用默認值。

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"

嵌套函數

函數可以在它的内部包含一個或多個函數。内部函數可能會在内部再次包含函數。讓我們來看看以下操作。

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

函數wakeUpAndCode包含兩個内部函數wakeUp和code。當調用wakeUpAndCode時,函數主體開始執行函數主體。在外部函數中只有兩個可執行語句,調用wakeUp和code的方法。調用wakeUp將執行内部wakeUp函數,這將寫入string“I just woke up”到控制台。調用code將會寫入“I am ready to code now”string到控制台。

内部函數可以訪問所有外部函數的變量和參數。内部函數是函數内部某種private實現,並且不能從外部函數以外被調用。内部函數的使用生成了JavaScript閉包,這個我將另起一篇文章討論。

立即執行函數表達式(IIFE,發音iffy)

IIFE是被立即調用執行的匿名函數表達式。IIFE看上去像這樣:

(function() {
    // Your awesome code here
}());

所有你要做的就是創建一個匿名函數,在函數定義後馬上放一對圓括號以調用函數,最後將所有代碼封裝在另一對圓括號中。最外層的括號將它裡面的所有一切轉變成一個表達式,因爲括號不能包含JavaScript語句。函數定義後面的圓括號則立即調用函數。

IIFE塊中定義的任何變量或函數對塊而言是本地的,並且不能被這個範圍以外的任何代碼改變。

看看IIFE的這個例子。此函數沒有調用也會自動執行。

(function() {
    console.log("I run on my own.");
}());

只需在plunker中複制並粘貼代碼,看看在瀏覽器控制台中的輸出。如果你不知道去哪裡找瀏覽器控制台,那麽只要在瀏覽器窗口中按下F12就會出現開發者工具。跳轉console選項卡以查看console.log語句的所有輸出。

IIFE是一個在代碼中創建局部範圍的很好方法。它們可以幫助你保護變量和函數,以避免被應用程序的其他部分更改或覆蓋。JavaScript中IIFE的其他優勢?它們是如何解決全局範圍污染問題的?歡迎點擊查看我關於立即執行函數表達式的文章。

構造函數

函數可以充當構造器的角色,並且可以使用構造函數來創建新的對象。這是使JavaScript面向對象的特點之一。使用構造函數的好處是,你將能夠通過預定義的屬性和方法,創造盡可能多的對象。如果你由此關聯到其他語言中的類和對象,那麽你做的對。

讓我們創建一個帶有一些屬性和方法的構造函數Programmer。你可以假設它在你最喜歡的語言中是一個類。

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";
    }
}

函數有三個參數,並創建了一個具有三個屬性和四種方法的對象。我不認爲上面的代碼需要任何解釋。此外,我可以創建任意數量程序員對象。

var javaProgrammer = new Programmer("Mohit Srivastava", "Infosys", "Java");
var dotnetProgrammer = new Programmer("Atul Mishra", "Prowareness", ".NET");

雖然也可以創建一個使用對象文本語法帶有相同屬性和方法的對象,但我們需要多次編寫相同的代碼,這可不是什麽偉大的實踐。如果你知道編程DRY原則,那麽你就不會不贊同我。構造函數使得可以一次定義對象,並創建真正的實例,無論什麽時候你想要。

警告!

始終使用new關鍵字來從構造器創建新的對象。忘記了new而像這個創建一個實例->

var jsProgrammer = Programmer("Douglas Crockford", "Yahoo", "JavaScript")

最終將添加所有屬性和方法到全局的window對象,哇哦,這將是太可怕了。原因是,除非明確指定,否則“this”指向全局的window對象。使用new 設置“this”上下文到被創建的當前對象。

然而,有一種變通方法可以來克服這個問題。你可以改變構造函數的實現以使域安全,然後在創建新的對象時,你就可以愉快地忽略new 關鍵字了。請參見以下修改了的構造函數代碼。爲了便於查看,我已刪除了一些方法。

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 對象是否是Programmer的一個實例。如果不是,它會創建一個新的Programmer對象,並通過再次調用構造器返回相同的内容。

注意:你無法在不使用’new’關鍵字的情況下,在Strict模式下從構造器創建一個新的對象。Strict模式強制一些編碼準則,並且在你寫的東西不安全的情況下會拋出錯誤。要啓用Strict模式,你只需要添加在你的代碼開頭添加字符串 ‘use strict’。在Strict模式下運行代碼是一個良好的實踐。

'use strict' function doSomething() { ... } .... ....
閲完此文,您的感想如何?
  • 有用

    1

  • 沒用

    1

  • 開心

    1

  • 憤怒

    1

  • 可憐

    1

1.如文章侵犯了您的版權,請發郵件通知本站,該文章將在24小時内刪除;
2.本站標注原創的文章,轉發時煩請注明來源;
3.交流群: 2702237 13835667

相關課文
  • JS如何防止父節點的事件運行

  • nodejs編寫一個簡單的http請求客戶耑代碼demo

  • 說一則爲什麽後耑開發人員不選擇node.js的原因

  • 使用Sublime Text3 開發React-Native的配置

我要說說
網上賓友點評