函数:就是封装了一段可被重复调用执行的代码块。通过次代码块可以实现大量代码的重复使用。
5.1 声明函数
- 函数对任何语言来说都是核心组件,因为它们可以封装语句,任何在任何地方、任何时间执行。ECMAScript中的的函数使用
function
关键字声明,此关键字小写,后跟一组参数,然后是函数体。 函数的命名应该有意义,即能通过函数名来了解此函数的作用。
function functionName(arg0,arg1,···,argN) {
statements
}
function 函数名(形参0, 形参1, ···, 新参N) {
// 函数内部代码
}
5.2 调用函数
函数在声明后可以进行调用,调用没有次数限制,但先后顺序会影响结果
函数名(实参,实参); //函数调用时通过函数名来使用
调用函数时不要忘记后面的
()
- 函数需要通过调用来执行
函数内部声明的变量为局部变量,在函数外部无法使用,需要通过
return
来返回借给函数带到全局环境下。5.3 形参和实参
形参:时函数在构建声明时模拟带入的变量,不做声明,也没有任何实意
实参:字面意义,在调用函数时,带入函数内部的参数变量,通常在外部声明
// 构建函数
function 函数名(形参,形参) {
函数内部代码
}
//调用函数
函数名(实参,实参)
函数形参和实参数量不匹配时
5.4 函数返回值
函数尽量不要再内部进行输出,尤其在进行多个函数参与情况下建议函数只需返回运算结果
function 函数名(形参,形参){
函数运算;
return 返回给函数的值
}
在使用 return 语句时,函数会停止执行,并返回指定的值
如果函数没有 return,返回的值是 undefined
function sun(num1, num2) {
return num1 + num2; //将两个数的和返回给函数
console.log(num1); //return 后面的函数内部代码不会被执行
return只能返回一个值,如果填写多个值,只会返回最后一个逗号后面的值
- 如果需要返回多个值,可以使用数组进行多个值的返回
-
break、continue、return的区别
break:结束当前的循环体(如 for、while)
- continue:跳出本次循环,继续继续下次循环(如 for、whlie)
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);
}
}
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(sum1,sum2) {
retun 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;
};