创建函数

1、函数声明

  1. function testName(){
  2. // 执行语句
  3. }
  4. // 调用函数
  5. testName();
  6. // 每调用一次函数,函数内的代码都会重新执行一次
  7. testName();
  8. testName();

「函数声明」其实有点类似于「变量声明」,不同的是变量声明用的是var关键字,而函数声明使用function关键字。

  1. var testName = 123;

2、函数表达式(字面量)

  1. var testName = function funName(){
  2. // 执行语句
  3. }
  4. testName(); // 正常执行
  5. funName(); // Error

当我们使用函数表达式创建函数的时候,JS会忽略funName然后使用testName去引用函数,testName才是真正要执行的函数。
由此我们可以简化函数表达式:

  1. var testName = function (){
  2. // 执行语句
  3. }
  4. testName();

如何理解函数字面量?
在我们声明变量的时候等于号右面就是字面量,字面量可以是任意的数据类型:

  1. var a = 123;
  2. var b = "str";
  3. var c = function(){}

3、函数命名的规则

  • 不能用数字开头
  • 可以用字母、_、$ 开头
  • 可以包含数字
  • 小驼峰命名,例如:myTestFunName

函数的参数

在函数中,()里存放的是函数的参数,参数可以丰富函数中的功能,完全由外部控制函数接收的数据。

  1. // param1,param2 就是函数的形参
  2. function testName(param1, param2){
  3. console.log(testName.length); // 返回函数形参的个数
  4. }

这里的参数是没有真正的数据的,只是形式上的占位,所以我们也叫函数的「形参」,形参的名字可以自定义命名。

  1. function testName(param1,param2){
  2. console.log(arguments); // 返回实参的数组
  3. }
  4. testName(1, 2, 3);

当我们调用函数的时候可以传入一些数值,这些数值是可以被函数实际使用的,所以我们称为「实参」。

函数的「实参」和「形参」需要一一对应起来,确保使用的形参就是你要操作的实参,两者的数量可以不相等,但是位置一定要相应。

  1. function testName(param1,param2){
  2. console.log(param1,param2); // 1,2
  3. }
  4. // 多传入的 3 不会被打印,也不会报错
  5. testName(1,2,3);
  6. function testName2(param1,param2,param3){
  7. // 未传入的参数会显示 undefind
  8. console.log(param1,param2,param3); // 1,2,undefind
  9. }
  10. testName2(1,2)

参数映射

在函数体内是可以更改实参传过来的值:

  1. function testName(param1, param2) {
  2. // 可以更改形参的值
  3. param1 = 3;
  4. console.log(arguments); // [3, 2]
  5. console.log(param1); // 3
  6. }
  7. testName(1,2);

在函数内打印arguments数组和param1虽然都是 3,但它们并不是同一个东西,它们之间只是一种映射关系,param1是保存在栈内存中,arguments是保存在堆内存中,栈内存保存了堆内存的地址。

映射关系:无论外部如何给实参赋值,函数的形参和 arguments 都会跟着改变,函数的形参和 arguments 的值始终是同步的。

如果没有给形参传值在函数内进行赋值arguments是不会显示的:

  1. function testName(param1, param2) {
  2. // 可以更改实参的值
  3. param2 = 3;
  4. console.log(arguments); // [1]
  5. console.log(param2); // 3
  6. }
  7. testName(1);

如果只传了一个实参,在函数内给第二个形参赋值,这个值并不会反映到第二个命名参数。这是因为 arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。

传递参数

ECMAScript 中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。如果是原始值,那么就跟原始值变量的复制一样,如果是引用值,那么就跟引用值变量的复制一样。
在按值传递参数时,值会被复制到一个局部变量( arguments对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部。

  1. function addTen(num) {
  2. num += 10;
  3. return num;
  4. }
  5. let count = 20;
  6. let result = addTen(count);
  7. console.log(count); // 20,没有变化
  8. console.log(result); // 30
  1. function setName(obj) {
  2. obj.name = "Nicholas";
  3. }
  4. let person = new Object();
  5. setName(person);
  6. console.log(person.name); // "Nicholas"

参数的默认值

在我们不给形参传实参的时候也可以给形参一个默认值

  1. // ES5 的写法
  2. function testName(a, b) {
  3. var a = arguments[0] || 1;
  4. var b = arguments[1] || 2;
  5. console.log(a, b);
  6. }
  7. testName(3); // 3,2
  8. testName(undefined, 4); // 1,4
  9. // ES6 的写法
  10. function testName(a = 1, b = 2) {
  11. console.log(a, b);
  12. console.log(arguments)
  13. }
  14. testName(3); // 3,2
  15. testName(undefined, 4); // 1,4

函数的返回值

函数可以使用return关键字将函数终止执行和返回数据。

  1. function testName(){
  2. console.log("我正在执行函数"); // "我正在执行函数"
  3. return;
  4. console.log("函数执行完成"); // 不会执行
  5. }
  6. testName();

如果你不显示的写出return,JS引擎会自动给你补全。

  1. function testName(){
  2. console.log("我正在执行函数");
  3. // return; // JS自动补全
  4. }
  5. testName(); // "我正在执行函数"

return还可以返回数据:

  1. function testName(){
  2. console.log("我正在执行函数"); // "我正在执行函数"
  3. return "这是返回值";
  4. console.log("函数执行完成"); // 不会执行
  5. }
  6. console.log(testName()) // "这是返回值"

如果不显式的返回内容,return会返回undefind

  1. function testName(){
  2. console.log("我正在执行函数"); // "我正在执行函数"
  3. return;
  4. }
  5. console.log(testName()) // undefind

递归

函数递归就是「函数自己调用自己」。

如何使用递归呢?
首先我们要确定函数执行的「规律」,最后还要给函数一个终止调用自己的「出口」。

因为递归是自己调用自己,一步一步等到下次调用返回的结果,这样就形成了嵌套,所以在性能上不太友好,仅适用处理简单的程序。

  1. // n 的阶乘
  2. function fact(n) {
  3. if (n === 1) {
  4. return 1;
  5. } else {
  6. return n * fact(n - 1);
  7. }
  8. }
  9. console.log(fact(5))
  10. // 斐波拉数列
  11. function fb(n) {
  12. if (n <= 2) {
  13. return 1;
  14. } else {
  15. return fb(n - 1) + fb(n - 2);
  16. }
  17. }
  18. console.log(fb(6));