函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
5.1 声明函数
- 函数对任何语言来说都是核心组件,因为它们可以封装语句,然后在任何地方、任何时间执行。ECMAScript 中的函数使用
function
关键字声明,此关键词小写,后跟一组参数,然后是函数体。 - 函数的命名应该有意义,即能通过函数名来了解此函数的作用
function functionName(arg0, arg1,...,argN) {
statements
}
function 函数名(形参0, 形参1,...,形参N) {
//函数内部代码
}
5.2 调用函数
函数在声明后可以进行调用,调用没有次数限制,但先后顺序会影响结果
函数名(实参,实参); //函数调用时通过函数名来使用
- 调用函数时不要忘记后面的
()
- 函数需要通过调用来执行
- 函数内部声明的变量为局部变量,在函数外部无法使用,需要通过
return
来返回结果给函数带到全局环境下。
5.3 形参和实参
- 形参:是函数在构建声明时模拟带入的变量,不做声明,也没有任何实意
- 实参:字面意义,在调用函数时,带入函数内部的参数变量,通常在外部声明
//构建函数 function 函数名(形参,形参){ 函数内部代码 } //调用函数 函数名(实参,实参)
函数形参和实参数量不匹配时
5.4 函数返回值 (return)
函数尽量不要在内部进行输出,尤其在进行多个函数参与情况下,建议函数只需返回运算结果
function 函数名(形参,形参){
函数运算;
return 返回给函数的值
}
- 在使用 return 语句时,函数会停止执行,并返回指定的值
如果函数没有 return ,返回的值是 undefined
function sum(num1,num2){ return num1+num2; //将两个数的和返回给函数 console.log(num1); //return 后面的函数内部代码不会被执行 }
return只能返回一个值,如果填写多个值,只会返回最后一个逗号后面的值
- 如果需要返回多个值,可以使用数组的进行多个值的返回
- 也可以通过对象(object)来返回多个值
break、continue、return的区别
- break :结束当前的循环体(如 for、while)
- continue :跳出本次循环,继续执行下次循环(如 for、while)
- return :不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码
5.5 arguments
当不确定有多少个参数传递的时候,可以用 arguments
来获取。在 JavaScript 中,arguments
实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参,arguments
将用户输入的参数以数组的方式存储起来,在使用时用arguments[]
的方式来使用。
function doAdd() {
if (arguments.length === 1) {
console.log(arguments[0] + 10);
} else if (arguments.length === 2) {
console.log(arguments[0] + arguments[1]);
}
}
doAdd(10); // 20
doAdd(30, 20); // 50
arguments
展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
- 具有 length 属性
- 按索引方式储存数据
- 不具有数 组的 push , pop 等方法
arguments可以配合形参使用:
function doAdd(num1, num2) {
if (arguments.length === 1) {
console.log(num1 + 10);
} else if (arguments.length === 2) {
console.log(arguments[0] + num2);
}
}
arguments 对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。来看下面的例子:
function doAdd(num1, num2) {
arguments[1] = 10;
console.log(arguments[0] + num2);
}
- 这个 doAdd()函数把第二个参数的值重写为 10。因为 arguments 对象的值会自动同步到对应的命名参数,所以修改 arguments[1]也会修改 num2 的值,因此两者的值都是 10。
- 但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。
- 另外还要记住一点:如果只传了一个参数,然后把 arguments[1]设置为某个值,那么这个值并不会反映到第二个命名参数。这是因为 arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。
- 对于命名参数而言,如果调用函数时没有传这个参数,那么它的值就是 undefined。这就类似于定义了变量而没有初始化。比如,如果只给 doAdd()传了一个参数,那么 num2 的值就是 undefined。
注意:在函数内部使用该对象,用此对象获取函数调用时传的实参。
5.6 函数可以进行多次的嵌套
函数内部可以调用另一个函数,在同一作用域代码中,函数名即代表封装的操作,使用函数名加括号即可以将封装的操作执行。
5.7 函数的两种声明方式
事实上,JavaScript 引擎在加载数据时对它们是区别对待的。JavaScript 引擎在任何代码执行之前,会先读取函数声明,并在执行上下文中生成函数定义。而函数表达式必须等到代码执行到它那一行,才会在执行上下文中生成函数定义。
来看下面的例子:
// 没问题
console.log(sum(10, 10));
function sum(num1, num2) {
return num1 + num2;
}
以上代码可以正常运行,因为函数声明会在任何代码执行之前先被读取并添加到执行上下文。这个过程叫作函数声明提升(function declaration hoisting)。
在执行代码时,JavaScript 引擎会先执行一遍扫描,把发现的函数声明提升到源代码树的顶部。因此即使函数定义出现在调用它们的代码之后,引擎也会把函数声明提升到顶部。如果把前面代码中的函数声明改为等价的函数表达式,那么执行的时候就会出错:
// 会出错
console.log(sum(10, 10));
let sum = function(num1, num2) {
return num1 + num2;
};
上面的代码之所以会出错,是因为这个函数定义包含在一个变量初始化语句中,而不是函数声明中。这意味着代码如果没有执行到这一行,那么执行上下文中就没有函数的定义,所以上面的代码会出错。这并不是因为使用 let 而导致的,使用 var 关键字也会碰到同样的问题:
console.log(sum(10, 10));
var sum = function(num1, num2) {
return num1 + num2;
};