理解 JavaScript 函数





5.00/5 (15投票s)
本文旨在介绍一位 Web 开发者必须了解的 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
。
下面的代码通过传递访客姓名作为参数来调用该函数。
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# 程序员吗?你见过带默认参数的函数吗?可能见过!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
函数,该函数会将字符串“I just woke up
”写入控制台。调用 code
将字符串“I am ready to code now
”写入控制台。
内部函数可以访问所有外部函数的变量和参数。内部函数在函数内部是一种私有
实现,无法从外部调用。内部函数的使用催生了 JavaScript 中的闭包,我将在另一篇文章中讨论。
立即调用函数表达式 (IIFE,发音为 iffy)
IIFE 是一个匿名函数表达式,它会被立即调用。IIFE 看起来像这样
(function() { // Your awesome code here }());
你所要做的就是创建一个匿名函数,在函数定义后立即加上一对括号来调用函数,最后将所有代码包装在另一对括号中。最外层的括号会将括号内的所有内容变成一个表达式,因为括号不能包含 JavaScript 语句。函数定义后的括号会立即调用该函数。
在 IIFE 块内定义的任何变量或函数都是该块的局部变量,并且此范围外的任何代码都无法更改它们。
看看这个 IIFE 的例子。该函数将自动执行,无需调用。
(function() { console.log("I run on my own."); }());
只需将代码复制并粘贴到 plunker 中,然后在浏览器控制台中查看输出。如果你不知道在哪里可以找到浏览器控制台,只需在浏览器窗口中按 F12 键即可打开开发者工具。切换到“控制台”选项卡,即可查看所有 console.log
语句的输出。
IIFE 是创建代码局部作用域的绝佳方式。它们有助于保护你的变量和函数不被应用程序的其他部分更改或覆盖。IIFE 在 JavaScript 中还有哪些其他优点?它们如何解决全局作用域污染问题?别忘了查看我关于立即调用函数表达式的文章。
构造函数
一个函数可以充当构造函数,并可以使用构造函数创建新对象。这是使 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
模式,只需在代码开头添加字符串 'use strict
'。在strict
模式下运行代码是一个好习惯。
'use strict' function doSomething() { ... } .... ....
在这篇内容详尽的文章中,我几乎涵盖了关于函数的所有内容。函数被认为是 JavaScript 中的一等公民。如果你想精通 JavaScript,理解函数可能是最重要的事情。
我希望我没有让你因为阅读这篇冗长的文章而抓耳挠腮 ,并且相信你已经详细了解了函数。如果你喜欢这篇文章,请分享。如果不喜欢,请留下评论,帮助我下次改进。下次再见。
你可能还对以下内容感兴趣