函数实际上也是对象,每个函数都是Function类型的实例,函数名就是指向函数对象的指针。
函数声明
function sum (num1, num2) {
return num1 + num2;
}
函数表达式
const sum = function (num1, num2) {
return num1 + num2;
};
函数声明和函数表达式几乎是等价的。
ES6新增,箭头函数
const sum = (num1, num2) => {
return num1 + num2;
}
参数应该使用()
包裹,如果只有一个参数,则可以省略()
返回值
不使用{}
包裹:意味着这个箭头函数的返回值就是这一条语句。
使用{}
包裹:
如果不含return关键字,则js会试图将{}
内的代码解释为一个对象。
如果包含return关键字,则返回值由return关键字决定。{}
内可以编写任意多条语句。
箭头函数不能使用arguments
(传入的参数列表)、super
(调用父类构造器)、new.target()
(判断new关键字的构造函数),不能用作构造函数,也没有prototype属性。
ECMAScript函数不关心传入参数的多少,所有传入的参数都会被放入一个类数组对象argements
中,可以通过[]
访问传入的参数,也可以使用argements.length
获取传入参数的个数。
箭头函数的参数
如果函数是由箭头函数定义的,那么传给函数的参数不能用argements
访问
默认参数
在定义函数时,给参数加上param=默认值
就可以定义默认参数,默认参数只有在该变量没有传入时生效。
argements
不会记录默认参数的值,它只反映传入的参数
参数的定义顺序是按函数参数从左到右定义的,所以后定义的默认参数可以使用前面的变量。
扩展操作符
使用...param
的语法可以获取任意长度的参数,也可以将一个可迭代对象展开。
const nums = [0, 1, 2, 3, 4, 5];
function sum (...nums) {
return nums.reduce((acc,num) => acc + num);
}
console.log(...nums) // 0 1 2 3 4 5
console.log(sum(...nums)) // 15
函数定义会进行声明提升,在函数被定义前调用函数也不会报错,函数表达式不会进行声明提升
就如同前面介绍的,函数也是一种对象,函数名就是指向函数的指针,也就是说,如果我们将函数的指针作为值传递的话,我们就可以在函数中传递函数。
当函数作为返回值时,每次调用一个函数都会将这个返回值函数返回。
function say1 () {
console.log('我是say1');
return function say2 () {
console.log('22222');
};
}
// receiver接收了say1()的返回值
let receiver = say1(); // 我是say1 调用了say1函数
// receiver等价于如下代码
// let receiver = function say2(){
// console.log('22222');
// }
// 调用receiver
receiver() // 22222
// 如果想直接调用say2而不使用中间变量存储的话
say1()() // 我是say1 22222
这个技术也是闭包的关键。
JavaScript 中的回调函数(Callback Function)是一种将函数作为参数传递给另一个函数,并在特定条件满足时(如异步操作完成、事件触发等)被调用的机制。它是实现异步编程的核心模式之一,尤其在单线程的 JavaScript 环境中,通过回调函数可以避免阻塞主线程,提升程序的响应性和效率。
将函数名作为参数传递给另一个函数,由另一个函数决定怎么使用传入的函数,这个使用者函数就被称为回调函数。
简单说来,回调函数就是把指向函数的指针传递到另一个函数内部,由于拥有了函数的指针,于是可以在另一个函数中调用被传入的函数。
常见的Array.prototype.map()
、reduce()
、filter()
、setTimeOut()
等方法都使用了回调函数。
arguments:和前面介绍的一样,arguments是一个类数组对象,他存储了传入对象的参数。
callee:arguments还有一个callee属性,这个属性是一个指向arguments对象所在函数的指针
在使用函数的递归调用时,如果使用函数名进行递归调用,那么这个函数名就是不能更改的,但使用arguments.callee属性就不会导致这个问题。
在严格模式下不能使用callee属性,可以使用命名函数表达式
const func = function f(n) {
if (n === 2)
return 2;
return n * f(n - 1);
}
在定义函数时参数列表使用f()
的形式,就可以通过f()
调用自己。
this:标准函数中this指向调用的对象,而箭头函数的this指向定义函数时的上下文。
在之后详解。
new.target:检测函数是否是被new关键词调用的,如果被new关键字调用,那么值为被调用的构造函数,如果不是被new关键字调用的,则为undefined。
每个函数都有两个属性
length:保存了函数定义时,命名参数的个数
prototype:指向函数原型的指针
尾调用(Tail Call)是指函数的最后一步操作是调用另一个函数,且无需对返回值进行额外操作。例如:
function foo(x) {
return bar(x); // 尾调用,直接返回 bar(x) 的结果
}
尾调用优化(TCO)是 JavaScript 引擎对符合尾调用条件的函数进行栈帧复用的机制。其核心原理是:
当函数 A 的最后一步调用函数 B 时,引擎会直接复用 A 的栈帧,而非创建新的栈帧
这减少了调用栈的深度,避免了递归导致的栈溢出(Stack Overflow),并降低内存消耗
尾调用优化的条件
要使 JavaScript 引擎触发尾调用优化,需满足以下条件:
严格模式:必须启用 "use strict"
最后一步调用:调用必须是函数的最后一步操作,返回值直接来自被调用函数,不能有后续计算(如 return g(x) + 1
不满足)
无闭包依赖:被调函数不能依赖当前函数的局部变量(否则需保留栈帧)