目录
16-1 使用箭头函数表达式定义函数
16-2 使用 Function 构造函数定义函数
16-3 使用arguments对象获取传递给函数的值
16-4 获取表示函数定义的字符串
16-5 获取函数接收的参数数量
16-6 使用递归函数
16-7 使用回调函数/高阶函数
箭头函数表达式的使用简化了使用函数文字的函数定义。本节介绍如何在 JavaScript 中使用箭头函数表达式定义函数。
使用箭头函数表达式定义函数
通过使用箭头函数表达式,您可以更简单地编写带有函数字面量的函数定义。我使用以下格式使用函数文字定义函数。
let 变量名 = function(参数1, 参数2, ...){
要执行的处理;
...
return 返回值;
};
使用箭头函数表达式,我们可以写:
let 变量名 = (参数1, 参数2, ...) => {
要执行的处理;
...
return 返回值;
};
由于函数名称没有写在箭头函数表达式中,因此它们的使用方式与函数文字相同,是通过将它们分配给变量来使用的。也不要在箭头函数表达式中写函数,而是在函数块之前写 => 。
如果函数不接受参数,则只写括号。
let 变量名 = () => {
要执行的处理;
...
return 返回值;
};
要调用使用箭头函数表达式定义的函数,请编写:
let 变量名 = (参数1, 参数2, ...) => {
要执行的处理;
...
};
变量名(参数, ...);
请看下面的示例。
let dispTotal = (x, y) => {
return x + y;
};
let result = dispTotal(3, 4);
console.log(result);
>> 7
在此示例中,函数接收两个参数并将两个参数相加的结果返回给函数的调用者。函数的调用者用两个值调用函数并接收结果。
箭头函数表达式的简写
箭头函数表达式提供了一种更缩写的方式。如果只有一个参数,参数周围的括号 ( 和 ) 可以省略。
let 变量名 = 参数 => {
要执行的处理;
...
return 返回值;
};
如果功能块中要执行的处理只有return语句,可以这样写,省略代表块的{和},同时也省略return。
let 变量名 = (参数1, 参数2, ...) => 返回值;
如果只有一个参数,功能块中唯一要执行的过程就是return语句,可以这样写。
let 变量名=参数=>返回值;
让我们用缩写重写前面的例子。
let dispTotal = (x, y) => x + y;
let result = dispTotal(7, 2);
console.log(result);
>> 9
箭头函数表达式的使用使我们能够相当简洁地编写函数定义。但是,如果您不知道箭头函数表达式,可能很难理解正在定义该函数。
箭头函数表达式可用于简洁地定义函数,因此在将函数作为参数传递时直接编写函数时非常有用。
function dispNum(x, y, func){
console.log(func(x, y));
}
dispNum(7, 3, (x, y) => (x + y) / 2);
>> 5
在此示例中,使用箭头函数表达式定义的函数在调用另一个函数时被描述为参数。被调用函数执行块内作为参数传递的函数。
使用箭头函数表达式的注意事项
使用箭头函数表达式时有两个注意事项。
首先,您不能在使用箭头函数表达式定义的函数内部使用参数对象。arguments 对象是一个特殊的对象,它保存着从调用者传递给函数的值。如果在用箭头函数表达式定义的函数内部使用,则会发生错误。
let dispTotal = () => {
let sum = 0;
for (let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
console.log('sum = ' + sum);
};
dispTotal(4, 2, 7);
>> Uncaught ReferenceError: arguments is not defined
不过从调用者传过来的值可以像往常一样作为参数接收,而且在现在的JavaScript中,变长参数函数可以用于个数不固定的参数,所以应该不会有问题
第二点要注意的是当返回一个对象字面量作为返回值时。在箭头函数表达式的情况下,如果函数块中要执行的唯一处理是return语句,则可以这样写,省略表示块的{和},同时也省略return。
(x, y) => x + y;
这时候,如果返回值是对象字面量,不用写{属性名:值},需要把整个括在括号里,写成({属性名:值})。
誤: (x, y) => {name:x, old:y};
正: (x, y) => ({name:x, old:y});
这是因为用于表示对象文字的 { 和 } 被视为箭头函数表达式块。
JavaScript 中的函数是 Function 对象,它是内置对象之一。到目前为止,我们已经了解了如何使用 function 关键字定义函数以及如何使用函数文字定义函数,但您也可以使用 Function 对象的构造函数来定义函数。在这里,我们将看到如何使用 Function 构造函数定义函数。
使用 Function 构造函数定义函数
使用 Function 构造函数定义函数的方法使用以下语法:
let 变量名 = new Function('参数1', '参数2', ..., '要执行的处理');
使用 Function 构造函数时不要使用函数名称。在 new Funciton 之后指定 ( 和 ) 之间的参数。请注意,参数被指定为字符串。最后,当函数被调用时,以同样的方式将要执行的进程指定为字符串。
使用 Funciton 构造函数创建的函数对象通过将其分配给变量来使用。
由于new是可选的,所以也可以这样写。
Function('参数1', '参数2', ..., '要执行的处理');
您可以用逗号 (,) 分隔多个参数并将它们写在一个字符串中,而不是一个一个地写参数。。
Function('参数1,参数2,参数3', '要执行的处理');
如果函数不接收任何参数,则可以省略参数部分。只要确保描述调用函数时要执行的处理即可。
new Function('要执行的流程');
如果您需要在调用函数时执行多个操作,请将它们写在一个字符串中,以分号 (;) 分隔。
new Function('参数', ..., '要执行的流程1;要执行的流程2;要执行的流程3');
调用使用 Function 构造函数定义的函数
要调用使用 Function 构造函数定义的函数,请编写:
let 变量名 = new Function('参数', ..., '要执行的程序');
变量名(参数, ...);
要调用一个函数,在描述函数分配给的变量名之后,在 ( 和 ) 之间描述要传递给函数的参数。如果有多个参数,用逗号(,)分隔。如果没有参数,则无需写入任何内容,但即使在那种情况下也必须写入 ( 和 )。
现在让我们看看如何实际使用它。请看下面的示例。
let dispTotal = new Function('x', 'y', 'let sum = x + y;return sum');
let result = dispTotal(3, 4);
console.log(result);
>> 7
在此示例中,函数接收两个参数并将两个参数相加的结果返回给函数的调用者。函数的调用者用两个值调用函数并接收结果。
虽然在细节上有所不同,但使用 Function 构造函数创建函数与使用其他方法创建函数并没有太大区别。我觉得这个方法用的不多,所以用function关键字声明函数的方法或者用函数字面量定义函数的方法会比用这个方法好,我觉得不是。
传递给函数的值按顺序存储在参数中,但您可以使用参数对象获取调用者传递的值的数量和实际值。下面介绍如何使用 arguments 对象获取传递给函数的值。
什么是参数对象
arguments 对象是仅在函数内部可用的对象。它存储了所有传递给函数的值,你可以通过arguments[0],arguments[1],……的形式获取传递给函数的值,就像数组元素一样增加。请注意,arguments 对象不能在箭头函数中使用。
通过查看 arguments 对象的 length 属性,您可以看到实际传递给函数的值有多少。
如果一个函数接收到的值的个数不确定,可以使用arguments对象按顺序检索。可以接收一个不确定个数的值而不 (详见“变长参数说明” )。
现在让我们来看看如何使用参数对象。
获取实际传递的参数个数(length)
在 JavaScript 中,即使函数被定义为接收,例如,两个参数,如果调用函数时传递给函数的值的数量多于或少于两个,也不会发生错误。
function dispProfile(name, old){
console.log('name : ' + name);
console.log('old : ' + old);
}
dispProfile('Yuhua', 24, 'Shanghai');
>> name : Yuhua
>> old : 24
dispProfile('Jiangming');
>> name : Jiangming
>> old : undefined
如果传递的值太多,它们根本就不会被使用,如果太少,没有存储值的参数是未定义的。
这样,在 JavaScript 中调用函数时,并不总是会传递函数端定义的参数个数。在这种情况下,您可以参考 arguments 对象的 length 属性来获取实际传递的值。格式如下。
arguments.length
由于 arguments 对象仅在函数内部可用,因此 length 属性也仅在函数内部可见。
看看下面的示例。
function dispProfile(name, old){
console.log('arguments.length : ' + arguments.length);
}
dispProfile('Yamada', 24, 'Tokyo');
>> arguments.length : 3
dispProfile('Suzuki');
>> arguments.length : 1
在这个例子中,当调用函数 dispTotal 时,它会查看参数对象的 length 属性的值,并向控制台打印出调用者实际传递给函数的值有多少。
获取调用者传递的值
从调用者传递过来的值存储在函数的arguments中描述的变量中,但它们也单独存储在arguments对象中,形式分别为arguments[0]、arguments[1]、……。得到。
看看下面的示例。
function dispProfile(){
console.log('name : ' + arguments[0]);
console.log('old : ' + arguments[1]);
}
dispProfile('Yamada', 24);
>> name : Yamada
>> old : 24
在这个示例中,函数定义中没有写参数,但是使用参数对象获取函数调用者传递的值并输出到控制台。
还有,通过使用arguments对象,即使arguments的个数不固定,也可以按顺序获取并处理传递的值。看看下面的示例。
function dispTotal(){
let sum = 0;
for (let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
console.log('sum = ' + sum);
}
dispTotal(1, 3, 5);
>> sum = 9
dispTotal(7, 12, 3, 2, 8);
>> sum = 32
本例中,使用arguments.length属性获取调用者传递的值个数,并将值个数按顺序相加的结果输出到控制台,所以调用函数时的值为输出。任何数字都可以。
使用参数对象的注意事项
到目前为止,我们已经尝试过使用 function 关键字进行函数声明的函数,但即使使用函数字面量定义它们,您也可以以相同的方式使用函数。
let dispTotal = function(){
let sum = 0;
for (let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
console.log('sum = ' + sum);
};
dispTotal(1, 3, 5);
>> sum = 9
即使函数是使用 Function 构造函数定义的,它也可以以相同的方式使用。
但是,如果你使用箭头函数表达式定义一个函数,你将无法使用 arguments 对象,并且会得到一个 arguments is not defined 的错误。
let dispTotal = () => {
let sum = 0;
for (let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
console.log('sum = ' + sum);
};
dispTotal(1, 3, 5);
>> Uncaught ReferenceError: arguments is not defined
您可以使用 Function 对象的 toString 方法来获取函数定义内容的字符串表示形式。每当需要一个函数的字符串表示时,例如当连接一个字符串和一个函数时,就会使用可以使用 toString 方法获得的字符串。在这里,我们将解释如何使用 Function 对象的 toString 方法获取表示函数定义的字符串。
将函数转换为字符串 (toString)
toString 方法用于将函数转换为字符串。格式如下。
函数名.toString()
如果函数是使用function关键字声明的,函数名将被返回。如果函数是使用函数字面量定义的,在分配给函数对象的变量名上使用toString方法将返回函数定义的内容。返回一个代表的字符串
让我们按顺序试试。如果先声明函数。
function dispTotal(x, y){
let sum = x + y;
return sum;
}
console.log(dispTotal.toString());
>> function dispTotal(x, y){
>> let sum = x + y;
>> return sum;
>> }
现在有了一个函数文字:
let dispTotal = function(x, y){
let sum = x + y;
return sum;
}
console.log(dispTotal.toString());
>> function(x, y){
>> let sum = x + y;
>> return sum;
>> }
现在使用箭头函数表达式:
let dispTotal = (x, y) => {
let sum = x + y;
return sum;
}
console.log(dispTotal.toString());
>> (x, y) => {
>> let sum = x + y;
>> return sum;
>> }
最后,在使用 Function 构造函数时。
let dispTotal = new Function('x', 'y', 'let sum = x + y;return sum');
console.log(dispTotal.toString());
>> function anonymous(x,y
>> ) {
>> let sum = x + y;return sum
>> }
在以四种方式中的每一种方式定义函数之后,我对函数名称或我将函数对象分配给的变量名称运行了 toString 方法。基本上,函数定义的内容是一个字符串,但只有在使用 Function 构造函数时,才会返回与函数名称为 ‘anonymous’ 的函数声明相同的结果。
您可以通过引用 Function 对象中包含的 length 属性来获取函数接收的参数数量。这不是实际接收到的参数个数,而是函数定义时写入的参数个数。在这里,我们将看到如何使用 Function 对象的 length 属性获取函数参数的数量。
获取参数数量(length)
Function 对象的 length 方法用于查看函数定义为接受多少个参数。格式如下。
函数名.length
获取函数定义时接收的参数数量,而不是函数接收的实际参数。
看看下面的示例。
function dispTotal(x, y, z){
let sum = x + y + z;
return sum;
}
console.log(dispTotal.length);
>> 3
函数 dispTotal 被定义为采用三个参数。所以函数的 length 属性是 3。
如果参数有默认值
如果任何参数具有默认值,则 length 属性返回参数的数量,直到并包括第一个默认参数。看看下面的示例。
function dispPersonal(old, name = 'none', address){
console.log('old = ' + old);
console.log('name = ' + name);
console.log('address = ' + address);
}
console.log(dispPersonal.length);
>> 1
函数 dispPersonal 被定义为采用三个参数,但由于第二个参数具有默认值,因此 length 属性将是设置默认值的参数定位最后一个参数,所以为结果为1.
可変長引数が含まれている場合
length 属性不包括可变参数,因此它返回不包括可变参数的参数数。看看下面的示例。
function calcSum(start, ...num){
let sum = start;
for (let i = 0 ; i < num.length ; i++){
sum += num[i];
}
return sum;
}
console.log(calcSum.length);
>> 1
函数 calcSum 被定义为采用一个常规参数和可变数量的参数。length 属性为 1,因为它不包含可变长度参数。
递归函数是在函数内调用自身的函数。在这里,我将解释如何在 JavaScript 中使用递归函数以及在什么情况下可以使用它们。
如何编写递归函数
递归函数是从函数内部调用自身的函数。请参见以下示例。
function test(){
test();
}
test();
函数测试在函数内调用自己的测试函数。以这种方式调用自己的函数的函数称为递归函数。
递归函数的使用没有明确的规则,但是需要写一个说明,当满足这个条件时终止对自身的调用,这样就不会出现死循环。请看下面的示例。
function test(){
if (条件表达式) {
test();
}
return;
}
test();
在此示例中,它在条件表达式满足时递归调用自己的测试函数,但在条件表达式不再满足时停止调用自身并终止函数。有关更具体的示例,请参见以下示例。
function test(n){
console.log('Hello');
if (n != 0) {
test(n - 1);
}
console.log('Bye');
}
test(2);
>> Hello
>> Hello
>> Hello
>> Bye
>> Bye
>> Bye
在这种情况下,按顺序执行以下处理。
1) 测试函数 (1) 被调用。2 作为参数传递。
2)打印出Hello
3)测试函数(1)中的条件表达式变为2 != 0,变为真
4) 调用测试函数(2)。1 作为参数传递
5)打印出Hello
6)测试函数(2)中的条件表达式变为1 != 0,变为真
7) 调用测试函数(3)。0 作为参数传递
8) 打印出Hello
9) 测试函数(3)中的条件表达式为0 != 0 and false
10)打印出Bye
11)测试函数(3)结束
12)打印出Bye
13)测试函数(2)结束
14)打印出Bye
15)测试函数(1)结束
16) 移动到第一次调用该函数的下一个进程
结果,控制台打印了 3 次 Hello 和 3 次 Bye。这样每次递归调用函数时,条件表达式都会发生变化,最终函数不再被递归调用。当最后一个函数调用结束时,进程进入下一个执行函数调用的进程,结果函数调用按顺序结束。最后,第一个被调用函数的调用结束,启动下一个调用该函数的进程。
如果您发现递归函数的流程令人困惑,请考虑以下事项:
function testC(n){
console.log('Hello');
if (n != 0) {
// testD(n - 1);
}
console.log('Bye');
}
function testB(n){
console.log('Hello');
if (n != 0) {
testC(n - 1);
}
console.log('Bye');
}
function testA(n){
console.log('Hello');
if (n != 0) {
testB(n - 1);
}
console.log('Bye');
}
testA(2);
首先调用函数testA,在函数testA内调用函数testB,在函数testB内调用函数testC。在函数testC中,条件表达式变为false,所以不再调用其他函数,按照调用顺序结束函数处理。
递归函数是调用自己函数的函数,而不是像这样编写多个函数来做同样的事情。如果你不习惯,处理的流程很难理解,但如果你认为有另一个和你一样的函数调用它,可能会更容易理解。
递归函数的具体用法
递归函数的使用范围很广,从简单的阶乘计算到复杂的算法解决方案。
例如,阶乘是计算数字 5 的阶乘得出 5 * 4 * 3 * 2 * 1 = 120。使用递归函数计算得出:
function calc(n){
if (n == 0){
return 1;
}
return n * calc(n - 1);
}
console.log(calc(5));
>> 120
在此示例中,每次调用函数时都会执行以下计算。
1)调用calc(4)计算5 * calc(4)作为函数的返回值
2) 调用 calc(4) 并调用 calc(3) 计算 4 * calc(3)
3) 调用 calc(3) 并调用 calc(2) 计算 3 * calc(2)
4) 调用 calc(2) 并调用 calc(1) 计算 2 * calc(1)
5)调用calc(1),调用calc(0)计算1 * calc(0)
6) 调用 calc(0) 并执行 return 1
7) return 1 * 1 被执行
8) return 2 * 1 * 1 被执行
9) return 3 * 2 * 1 * 1 被执行
10) return 4 * 3 * 2 * 1 * 1 被执行
11) return 5 * 4 * 3 * 2 * 1 * 1被执行
12) 120 打印到控制台
在函数内部计算返回值的地方,我们调用的是函数本身。函数被递归调用,直到条件表达式为真,当递归函数不再被调用时,依次计算返回值,最后返回给调用者。
像这样的阶乘计算可以不使用递归函数来计算,但是如果你记得如何使用递归函数,你可能能够非常简洁地描述这个过程。
在 JavaScript 中,函数也是对象之一,可以在调用函数时将函数指定为参数,也可以在函数内部将函数作为返回值返回。以这种方式接收和返回函数的函数称为高阶函数。作为参数传递给函数并从函数内部调用的函数称为回调函数。在这里,我将解释如何在 JavaScript 中使用回调函数和高阶函数,以及在什么情况下可以使用它们。
如何编写回调函数/高阶函数
函数在 JavaScript 中也是对象,可以存储在变量中或在调用函数时指定为参数。请参见以下示例。
const price = function(price){
console.log('番茄价格为' + price + ' 元。');
}
price(100);
>> 番茄价格为 100 元。
我们使用函数文字来定义函数并将其存储在变量价格中。之后,调用存储在变量中的函数。在 JavaScript 中,函数可以存储在变量中。
现在看下面的示例。
function tomato(price, func){
const name = '番茄';
func(name, price);
}
const price = function(name, price){
console.log(name + ' 价格为 ' + price + ' 元。');
}
tomato(100, price);
>> 番茄价格为100 元。
在这个示例中,当调用函数 tomato 时,除了数值之外,它还传递单独定义的函数 price 作为参数。番茄函数调用块中接收到的函数。这样,在 JavaScript 中,一个单独定义的函数可以在调用另一个函数时作为参数传递。
作为参数(在本例中为价格)传递的函数称为回调函数,而接收另一个函数作为参数(在本例中为番茄)的函数称为高阶函数。
注意回调函数也可以直接写在参数的地方。
function tomato(price, func){
const name = '番茄';
func(name, price);
}
tomato(100, function(name, price){
console.log(name + ' 价格为 ' + price + ' 元。');
});
>> 番茄价格为100 元。
使用箭头函数看起来像这样:
function tomato(price, func){
const name = '番茄';
func(name, price);
}
tomato(100, (name, price) => {
console.log(name + ' 价格为' + price + ' 元。');
});
>> 番茄价格为100 元。
请记住,这两种描述方法都是常用的。
主要使用回调函数的情况
回调函数主要在进行异步处理时使用,当被调用函数处理完成后想执行指定为回调函数的函数时使用。当您希望在经过一定时间后执行特定进程时,或指定单击按钮时要执行的进程时,通常会使用回调函数。
让我们使用 window.setTimeout 方法创建一个示例。setTimeout 方法是等待指定秒数后执行作为参数指定的回调函数的方法。
setTimeout(function, delay);
将回调函数指定为第一个参数,以毫秒为单位的等待秒数(1/1000 秒)作为第二个参数。看看下面的示例。
const dispBye = function(){
console.log('Bye.');
}
console.log('Good Morning.')
setTimeout(dispBye, 3000);
console.log('How are you?');
>> Good Morning.
>> How are you?
>> Bye.
当我们运行示例时,它会打印“Good Morning”,然后调用 setTimeout 方法。参数中指定的回调函数在等待 3 秒后调用,但由于它是异步的,因此即使等待 3 秒,下一个过程也会按顺序执行,因此先输出“How are you?”。调用 setTimeout 方法 3 秒后,调用回调函数并执行“Bye.”。
这次是一个简单的示例,但是通过使用回调函数的机制,可以在被调用的高阶函数处理完成后,轻松调用并执行指定的函数。