声明函数
如何声明函数
函数使你能够将一段代码封装起来,并在程序中使用(经常会重复利用)。
有时候,函数具有参数,例如这节课开头部分的 pizza 按钮。reheatPizza()
具有一个参数:披萨块数。
function reheatPizza(numSlices) {
// code that figures out reheat settings!
}
你还见过的 reverseString()
函数具有一个参数:要倒转的字符串。
function reverseString(reverseMe) {
// code to reverse a string!
}
在这两种情况下,参数都作为变量在花括号里列在函数名称后面。此外,如果有多个参数,直接用逗号分隔就行。
function doubleGreeting(name, otherName) {
// code to greet two people!
}
但是,函数也可以没有任何参数。直接封装一些代码并执行某项任务。在这种情况下,直接将小括号留空就行了。例如,下面这个函数直接输出 "Hello!"
。
// accepts no parameters! parentheses are empty
function sayHello() {
var message = "Hello!"
console.log(message);
}
如果你将上述任何一个函数复制到 JavaScript 控制台中,可能不会注意到任何情况。实际上,可能会看到返回了 undefined
。当控制台无法明确返回任何内容时,使用特殊的 return
关键字就会出现默认的undefined
返回值。
返回语句
在上述 sayHello()
函数中,我们使用 console.log
向控制台输出了值,但是没有用返回语句明确返回内容。你可以使用关键字 return
,后面紧跟着你要返回的表达式或值写一个返回语句。
// declares the sayHello function
function sayHello() {
var message = "Hello!"
return message; // returns value instead of printing it
}
如何运行函数
现在,为了让函数执行任务,你需要调用函数,方法是使用函数名称,后面是小括号,其中包含任何传入的参数。函数就像机器,你可以构造机器,但是如果不开机的话,机器肯定不能运转。下面的示例演示了如何调用之前的 sayHello()
函数,然后将返回值输出到控制台中:
// declares the sayHello function
function sayHello() {
var message = "Hello!"
return message; // returns value instead of printing it
}
// function returns "Hello!" and console.log prints the return value
console.log(sayHello());
输出:"Hello!"
Parameter 与 Argument
一开始很难判断某项内容是 parameter 还是 argument。关键区别在于它们出现在代码中的何处。parameter 始终是变量名称,并出现在函数声明中。相反,argument 始终是一个值(即任何 JavaScript 数据类型:数字、布尔值等),并且始终出现在函数调用代码中。
请试着自己声明和调用一些函数:
习题 1/2
请使用以下函数回答此问题。
function findAverage(x, y) {
var answer = (x + y) / 2;
return answer;
}
var avg = findAverage(5, 9);
变量 avg
中存储的将是什么值?
-
"answer"
-
(x + y) / 2
-
7
-
14
-
4
习题 2/2
function findAverage(x, y) {
var answer = (x + y) / 2;
return answer;
}
var avg = findAverage(5, 9);
对于此函数来说,x
和 y
是 parameter 还是 argument?
-
Parameters
-
Arguments
-
函数总结
到目前为止所学的知识:
函数会封装代码,使你能够轻松地使用(重复使用)一段代码。 Parameter 属于变量,用于存储传递到函数中的数据,并供函数使用。 Argument 是当函数被调用时传递到函数中的实际数据:
// x and y are parameters in this function declaration
function add(x, y) {
// function body
var sum = x + y;
return sum; // return statement
}
// 1 and 2 are passed into the function as arguments
var sum = add(1, 2);
函数主体部分位于花括号里:
function add(x, y) {
// function body!
}
返回语句明确规定函数返回一个值:
return sum;
通过调用函数使其执行某项任务:
add(1, 2);
Returns: 3
练习:哈哈大笑 1 (5-1)
说明:
声明一个叫做 laugh()
的函数并返回 "hahahahahahahahahaha!"
。将 laugh()
函数返回的值输出到控制台中。
你的代码:
练习:哈哈大笑 2 (5-2)
说明:
写一个叫做 laugh()
的函数,并且具有一个 parameter num
,表示要返回的 "ha"
的数量。
提示:要解决这道题,你可能需要使用循环!
下面是个输出示例,并且演示了如何调用你将写的函数:
console.log(laugh(3));
Prints: "hahaha!"
你的代码:
返回值
请务必明白返回和输出并不是一回事。向 JavaScript 控制台输出值仅显示一个值(你可以查看该值并调试代码),但是该值的作用也仅此而已。因此,务必仅使用 console.log
在 JavaScript 控制台中测试你的代码。
请将以下函数声明和函数调用代码粘贴到 JavaScript 控制台中,看看日志(输出)和返回之间的区别:
function isThisWorking(input) {
console.log("Printing: isThisWorking was called and " + input + " was passed in as an argument.");
return "Returning: I am returning this string!";
}
isThisWorking(3);
输出: "Printing: isThisWorking was called and 3 was passed in as an argument"
返回: "Returning: I am returning this string!"
如果你没有明确定义返回值,函数默认地将返回 undefined
。
function isThisWorking(input) {
console.log("Printing: isThisWorking was called and " + input + " was passed in as an argument.");
}
isThisWorking(3);
输出: "Printing: isThisWorking was called and 3 was passed in as an argument"
返回: undefined
习题 1/3
该函数返回的是什么?
function sleep() {
console.log("I'm sleepy!");
return "zzz";
return "snore";
}
sleep();
-
"I'm sleepy!"
-
"zzz"
-
"snore"
习题 2/3
函数将向 JavaScript 控制台输出什么数字?
function square(x) {
return x * x;
}
function subtractFour(x) {
return square(x) - 4;
}
console.log(subtractFour(5));
-
25
-
1
-
21
-
5
习题 3/3
下面的代码会出现什么结果?
function test() {
return 1;
return 2;
}
test();
-
1
will be returned -
2
will be returned -
3
will be returned -
error
使用返回值
使用返回值
函数能够返回值很不错,但是如果不使用该值执行某项操作,又有什么用呢?
函数的返回值可以存储在变量中或在整个程序期间作为参数使用。此处有个将两个数字相加的函数,另一个函数将数字除以 2。我们可以通过以下方法求出 5 和 7 的平均值:使用 add()
函数将一组数字相加,然后将 add(5, 7)
求出的两个数字的和作为参数传入函数 divideByTwo()
中。
最后,我们甚至可以将最终答案存储到变量 average
中,并使用该变量执行更多的运算!
// returns the sum of two numbers
function add(x, y) {
return x + y;
}
// returns the value of a number divided by 2
function divideByTwo(num) {
return num / 2;
}
var sum = add(5, 7); // call add function is stored in the sum variable
var average = divideByTwo(sum); // call divideByTwo function and store in answer variable
练习题
试着预测下下面的 console.log
语句将输出什么。然后,将这段代码粘贴到 JavaScript 控制台中,检测下你的预测结果。函数比较难懂,所以先试着推断出结果,然后再去运行代码!
function addTen(x) {
return x + 10;
}
function divideByThree(y) {
return y / 3;
}
var result = addTen(2);
console.log(divideByThree(result));
-
2
-
10
-
12
-
4
作用域示例
提示:你将学习第三种作用域类型,即新版本的 JavaScript 中的块作用域。请参阅我们的 ES6 课程了解详情!
习题 1/2
下面的哪个变量是在全局作用域中定义的:a
、b
、c
或 d
?
var a = 1;
function x() {
var b = 2;
function y() {
var c = 3;
function z() {
var d = 4;
}
z();
}
y();
}
x();
-
a
-
b
-
c
-
d
习题 2/2
可以在何处输出变量 c
的值而不产生错误?
var a = 1;
function x() {
var b = 2;
function y() {
var c = 3;
function z() {
var d = 4;
}
z();
}
y();
}
x();
-
anywhere in the script!
-
anywhere inside function
x()
-
anywhere inside function
y()
-
anywhere inside function
z()
覆盖
习题 1/2
不用将代码粘贴到控制台中,能判断出这段代码的输出内容码?
var x = 1;
function addTwo() {
x = x + 2;
}
addTwo();
x = x + 1;
console.log(x);
-
1
-
2
-
3
-
4
习题 2/2
不用将代码粘贴到控制台中,能判断出这段代码的输出内容码?
var x = 1;
function addTwo() {
var x = x + 2;
}
addTwo();
x = x + 1;
console.log(x);
-
1
-
2
-
3
-
4
全局变量
使用全局变量
你可能会问:
“为何不能始终使用全局变量呢?这样的话,我就不需要使用函数参数了,因为我的所有函数都可以访问所有内容了!"
虽然一开始会觉得全局变量是个很方便的方法,尤其是写些小程序,但是不应该使用全局变量有很多理由,除非必须使用。例如,全局变量会跟其他名称相同的全局变量产生冲突。程序变得越来越大后,就很难跟踪这些变量并防止出现这一情况。
你还会在更高级的课程中学到其他原因。但是就目前而言,尽量避免使用全局变量。
作用域总结
到目前为止所学的知识:
- 如果标识符在全局作用域内声明,则可以到处访问。
- 如果标识符在函数作用域内声明,则可以在所声明的函数内访问(甚至可以在函数中声明的函数内访问)。
- 尝试访问标识符时,JavaScript 引擎将首先查看当前函数。如果没找到任何内容,则继续查看上一级外部函数,看看能否找到该标识符。将继续这么寻找,直到到达全局作用域。
- 全局作用域不是很好的做法,可能会导致糟糕的变量名称,产生冲突的变量名称和很乱的代码。
提升
提升
有时候,JavaScript 代码可能会产生第一眼看着不明显的错误。提升可能是某个导致很难调试的错误的罪魁祸首。
习题 1/3
下面这段代码会向控制台中输出什么值?
sayHi("Julia");
function sayHi(name) {
console.log(greeting + " " + name);
var greeting;
}
-
error
-
undefined
-
Julia
-
undefined Julia
-
null
-
null Julia
-
NaN
-
NaN Julia
-
Hello Julia
习题 2/3
下面这段代码会向控制台中输出什么值?
sayHi("Julia");
function sayHi(name) {
console.log(greeting + " " + name);
var greeting = "Hello";
}
-
error
-
undefined
-
Julia
-
undefined Julia
-
null
-
null Julia
-
NaN
-
NaN Julia
-
Hello Julia
习题 3/3
下面这段代码会向控制台中输出什么值?
function sayHi(name) {
var greeting = "Hello";
console.log(greeting + " " + name);
}
sayHi("Julia");
-
error
-
undefined
-
Julia
-
undefined Julia
-
null
-
null Julia
-
NaN
-
NaN Julia
-
Hello Julia
提升总结
到目前为止所学的知识:
- JavaScript 会将函数声明和变量声明提升到当前作用域的顶部。
- 变量赋值不会提升。
- 在脚本的顶部声明函数和变量,这样语法和行为就会相互保持一致。
练习:构建三角形 (5-3)
说明:
对于这道练习,你将创建一个叫做 buildTriangle()
的函数,该函数将传入输入值(最大宽度的三角形),并构建一个三角形。请看下面的示例输出。
buildTriangle(10);
返回:
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
我们提供了一个 makeLine()
函数。该函数传入线条长度,并构建星号线条,返回具有换行符的线条。
function makeLine(length) {
var line = "";
for (var j = 1; j <= length; j++) {
line += "* "
}
return line + "\n";
}
你将在 buildTriangle()
中调用此函数。
这将是到目前为止你编写的最复杂的程序,所以在写代码前,先思考下。你需要使用 JavaScript 工具箱中的什么工具?专业人士在写代码之前会先规划好。思考下代码将执行的步骤,并按顺序写下来。然后从头到尾的检查该列表,并将每个步骤转换为实际的代码。祝你好运!
你的代码:
函数表达式
一旦你了解了如何声明函数,就会存在无数的可能性。
例如,还记得可以向变量中存储任何内容吗?在 JavaScript 中,你也可以向变量中存储函数。存储在变量中的函数叫做函数表达式。
var catSays = function(max) {
var catMessage = "";
for (var i = 0; i < max; i++) {
catMessage += "meow ";
}
return catMessage;
};
注意 function
关键字没有名称了。
var catSays = function(max) {
// code here
};
是一个匿名函数,即没有名称的函数,你将其存储在了叫做 catSays
的变量中。
如果尝试访问变量 catSays
的值,甚至会看到该函数返回给了你。
catSays;
Returns:
function(max) { var catMessage = "" for (var i = 0; i < max; i++) { catMessage += "meow "; } return catMessage; }
函数表达式和提升
何时使用函数表达式及何时使用函数声明取决于几项内容,你将在下个部分看到几种使用方法。但是,需要注意的一点是提升。
所有函数声明提升和加载后,脚本才会实际地运行。函数表达式不会提升,因为它们涉及变量赋值,只有变量声明会提升。在解析器在脚本中到达该表达式之前,函数表达式不会加载。
函数表达式模式
作为参数的函数
可以将函数存储在变量中使我们能够轻松地将函数传递到另一个函数中。传递到另一个函数中的函数叫做回调。假设有个 helloCat()
函数,你希望它返回 "Hello",后面跟着字符串“meows”,就像 catSays
一样。你可以使 helloCat()
接受回调函数,并传入 catSays
,而不用手动操作。
// function expression catSays
var catSays = function(max) {
var catMessage = "";
for (var i = 0; i < max; i++) {
catMessage += "meow ";
}
return catMessage;
};
// function declaration helloCat accepting a callback
function helloCat(callbackFunc) {
return "Hello " + callbackFunc(3);
}
// pass in catSays as a callback function
helloCat(catSays);
有名称的函数表达式
内嵌函数表达式
函数表达式是指将函数赋为变量值。在 JavaScript 中,当你将函数作为参数内嵌传递给其他函数时,也会发生这种情况。例如下面的 favoriteMovie
示例:
// Function expression that assigns the function displayFavorite
// to the variable favoriteMovie
var favoriteMovie = function displayFavorite(movieName) {
console.log("My favorite movie is " + movieName);
};
// Function declaration that has two parameters: a function for displaying
// a message, along with a name of a movie
function movies(messageFunction, name) {
messageFunction(name);
}
// Call the movies function, pass in the favoriteMovie function and name of movie
movies(favoriteMovie, "Finding Nemo");
返回:My favorite movie is Finding Nemo
但是你可以通过将函数内嵌传递给 movies()
函数,绕过第一个函数赋值。
// Function declaration that takes in two arguments: a function for displaying
// a message, along with a name of a movie
function movies(messageFunction, name) {
messageFunction(name);
}
// Call the movies function, pass in the function and name of movie
movies(function displayFavorite(movieName) {
console.log("My favorite movie is " + movieName);
}, "Finding Nemo");
Returns: My favorite movie is Finding Nemo
这种函数表达式,即将函数内嵌传递给其他函数的语法在 JavaScript 中很常见。一开始可能比较难懂,但是请保持耐心,不断练习,渐渐的就熟练了!
为何使用匿名内嵌函数表达式?
一开始可能觉得使用匿名内嵌函数表达式不太实用。为何定义一个只用一次,甚至不能通过名称调用的函数呢?
匿名内嵌函数表达式通常与可能不会在其他地方重复使用的函数回调一起使用。是的,你可以将该函数存储在变量中,设个名称,然后像上面看到的示例一样传入该函数。但是,当你知道不会重复使用该函数时,直接内嵌定义的话,会省去不少代码。
函数表达式总结
到目前为止所学的知识:
函数表达式:当将函数赋值给变量时,函数可以有名称,也可以是匿名的。 使用变量名称调用在函数表达式中定义的函数。
// anonymous function expression
var doSomething = function(y) {
return y + 1;
}
// named function expression
var doSomething = function addOne(y) {
return y + 1;
}
// for either of the definitions above, call the function like this:
doSomething(5);
返回:6
你甚至可以内嵌地将函数传入另一个函数中。这种模式在 JavaScript 中经常用到,有助于简化代码。
// function declaration that takes in two arguments: a function for displaying
// a message, along with a name of a movie
function movies(messageFunction, name) {
messageFunction(name);
}
// call the movies function, pass in the function and name of movie
movies(function displayFavorite(movieName) {
console.log("My favorite movie is " + movieName);
}, "Finding Nemo");
练习:laugh (5-4)
说明:
写一个匿名函数表达式,将该函数存储在叫做“laugh”的变量中,并输出当做参数传入的“ha”的数量。
laugh(3);
返回:hahaha!
你的代码:
练习:Cry (5-5)
说明:
写一个具有名称的函数表达式,将函数存储在叫做 cry
的变量中,并输出“boohoo!”。别忘了使用变量名称(不是函数名称)调用该函数:
cry();
返回:boohoo!
你的代码:
练习:内嵌 (5-6)
说明:
调用 emotions()
函数,使其输出下面显示的结果,但是传入内嵌函数表达式,而不是将 laugh()
函数当做参数传入。
emotions("happy", laugh(2)); // you can use your laugh function from the previous quizzes
输出:"I am happy, haha!"