JavaScript 基础(三)

584 阅读6分钟

函数

​ 函数就是功能,封装一些语句放在函数内部,函数就具有了某一特定的功能

声明:function 函数名() {}

调用:函数名();

函数基础

1. 函数的声明和调用

函数声明:定义函数

关键字:function

语法:

function 函数名(参数) { 结构体 }

调用:

函数名();

  • 函数名必须遵循标示符定义规则
  • 函数只能先声明,才能够调用
  • 函数声明时,内部的语句不会执行
  • 函数执行的位置与声明的位置无关,取决于调用的位置
  • 函数调用语法:函数名(实参);
  • 函数可以多次调用
function fun() {
  console.log(10);
}
console.log(1);
fun();
// 1
// 10

2. 函数的参数

​ 函数可以帮我们封装一些代码,代码可以穿重复调用,函数留了一个接口,就是我们的参数,可以通过参数的变化让我们的函数发生不同的作用。

​ 参数就是变量:命名规则与变量一样

形参:函数定义时,小括号里的参数叫做形式参数

实参:函数调用时,小括号的参数叫实际参数

传参:函数执行时,把实际参数传递到形式参数的过程

JavaScript 是一个动态类型数据语言,变量的类型会根据里面存放的内容而变化。实参的数据类型会影响形参的数据类型,导致函数输出结构发生变化。

function sum(a, b) {
  console.log(a + b);
}
sum(1, 2);
sum(1, '2');
sum(5); // 5 + undefined
// 12 number
// 12 string
// NaN

3. 函数返回值 return

函数可以使用 return语句接受参数进行操作,return 只是返回值,不会输出。

在函数中执行到 return关键字,会立即停止执行,直接返回

function sum(a, b) {
  return a + b;
  console.log(5); //不执行
}
console.log(sum(1, 2));
// 3

4. 模块化编程案例

案例1:判断1000以内得质数

​ 思路:判断一个数是不是质数 ===》 找这个数得约数个数

function YSGS(a) {
  var sum = 0;
  for (var i = 0; i <= a; i++) {
    if (a % i === 0) {
      sum ++;
    }
  }
  return sum;
}

function isZS(b) {
  return YSGS(b) === 2 ? true : false;
}

for (let i = 2; i < 1000; i++) {
  let msg = isZS(i) === true ?  '是质数' : '不是质数'
  console.log(i + msg);
}

案例2:1-1000中得完美数

​ 思路:判断一个数是不是完美数数 ===》 约束和等于本身

function YSH(a) {
  let sum = 0;
  for (let i = 1; i < a; i++) {
    if (a % i === 0) {
      sum += i;
    }
  }
  return sum;
}

function isWMS(a) {
  return YSH(a) === a ? true : false;
}

function WMS(total) {
  for (let i = 2; i < total; i++) {
    if (isWMS(i)) {
      console.log(i + '是完美数');
    }
  }
}

WMS(10000);
// 6是完美数
// 28是完美数
// 496是完美数
/// 8128是完美数

5. 函数表达式

函数定义可以使用关键字 function 来声明,也可以使用变量定义函数,称之为匿名函数

var sum = function(a, b) {
  return a + b;
};
console.log(sum(1, 2));
// 3

6. 函数的数据类型

  • 简单数据类型

    var a = 3;
    var b = a;
    a = 4;
    console.log(a);
    console.log(b);
    // 4
    // 3
    
  • 引用数据类型

    引用数据类型传递的是地址,之间相互影响,详情参考

    var fun1 = function () {
      console.log(1);
    }
    var fun2 = fun1
    fun2.haha = 10;
    console.log(fun1.haha); 
    // 10
    

7. 函数声明的提升

  • 变量声明得提升:先引用,后定义,只提升定义,不提升赋值

  • 函数声明得提升:先调用,后声明,只提升函数命,不提升定义,函数名指向函数得地址,所以调用函数时,函数内部得语句也会执行

根据这个特性,我们一般会先调用函数,在最后声明函数。

fun();
function fun() {
  console.log(10);
}
// 10

当函数和变量重名时

如果变量有值,那么输出的就是变量的值。

如果变量没有值,那么输出的就是这个函数。


var sum = 5;
function sum() {
  console.log(3);
}
console.log(sum);
 

var num;
function num() {
  console.log(3);
}
console.log(num);

console.log(a);
var a = 5;
function a() {
  console.log(3);
}

// 5
// [Function: num]
// [Function: a]

当关键字声明函数和函数表达式重名时,关键字声明会覆盖函数表达式

foo();
function foo() {
  console.log(5);
}
var foo = function () {
  console.log(3)
}
// 5

函数进阶

1. 递归函数

定义

在一个函数内部通过名字调用自身函数

一个函数可以调用自身,这中现场叫做 递归

应用

递归函数常用来处理一些数学问题

例如:斐波那契数列:1 1 2 3 5 8 13 21 34

// 用一个函数求斐波那契数列的任意项
function feibo(n) {
  for (let i = 0; i < n; i++) {
    if (n === 1 || n === 2) {
      return 1;
     } else {
       return feibo(n-1) + feibo(n-2)
     }
  }
}
console.log(feibo(9));
// 34

2. 变量的作用域

函数内部中使用关键字 var 定义的变量(函数作用域),只能在函数内部使用,不能在函数外部取得。

function fun() {
  var num = 1;
  console.log(num);
  // 1
}
console.log(num);
// num is not defined

3. 局部变量和全局变量

局部变量:在作用域(定义域)内定义的变量就是这个作用域的局部变量,只能在作用域内被访问到。

全局变量:全局变量定义在全局,可以在任何地方访问到。

变量声明原理:全局变量,在全局定义后会永久存在,任何时候任何位置都能访问;局部变量定义在函数内部,函数定义的过程并没有去定义这个局部变量,只有在函数执行的时,才会立即定义这个局部变量,执行完之后,变量回立即销毁,在其他地方访问时,找不到这个变量,所以报错该变量未定义。

4. 作用域链

作用域链指的是变量在查找的规律:在不同的作用域内使用相同的标识符去命名变量。若当前作用域内有这个变量,则直接使用;若没有,则会一层一层的从本层向外层依次查找,使用查找到的第一个(就近原则)

var num = 1;
function fun1() {
  var num = 2;
  function fun2() {
    var num = 3;
    console.log(num);
    // 本层中有定义直接输出 3
    function fun3() {
      console.log(num);
      // 本层没有定义,从本层出发依次向外查找,得到上层的 3
    }
    fun3();
  }
  fun2();
}
fun1();
console.log(num);
// 输出全局的 1

5. 形参是局部变量

function fun(a) {
  console.log(a);
  // 1
}
fun(1);
console.log(a);
// a is not defined

6. 全局变量的作用

  • 传递:全局变量可以在不同函数间通信(信号量)
var num = 1;
function jia() {
  console.log(num++);
}
function jian() {
  console.log(num--);
}
jia();  // 2
jia();  // 3
jian();  // 2
jian();  // 1
jia();  // 2
  • 同意函数不同调用
// 全局变量,不会让变量重置或者清空
var num = 1;
function plus() {
  num += 4;
  console.log(num++);
}
plus();  // 5
plus();  // 9
plus();  // 13

7. 函数作用域

函数作用域与变量类似,只能在函数声明的地方使用,外部的任何地方都访问不到

function outer() {
  function inner() {
    console.log(1);
  }
  inner();
}
outer();
// 1
inner();
// inner is not defined

8. 闭包

体会闭包

function outer() {
  var num = 1;
  function inner() {
    console.log(num);
  }
  // inner 没有括号表示只输出inner函数的定义,不会立即执行
  return inner;
}
console.log(outer());
/*
ƒ inner() {
  console.log(num);
}
*/
var fun = outer();
// fun相当于inner函数的定义(地址)
fun();
// 1
// 本层没有num的定义,但是inner存在作用域链,所以输出1

闭包 :函数把自己的 内部语句 和自己声明所出的 作用域 封装成一个密闭的环境

函数本身就是一个闭包。函数在定义的时候,能够记住他的外部环境和内部语句,每次执行都会参考定义时的密闭环境。

案例1

function outer(x) {
  function inner(y) {
    console.log(x + y);
  }
  return inner;
}
var fun = outer(3);
/*
  此时fun = function inner(y) {
    console.log(3 + y);
  }
*/
fun(5); // 8
fun(8); // 11

案例2

function outer(x, y) {
  function inner(x) {
    console.log(x + y);
  }
  return inner;
}
var fun = outer(2, 3);
/*
  inner的外部环境为
  x = 2;
  y = 3;
  fun = function inner(y) {
    此时x为传入的参数可以直接使用,
    x在该作用域不存在找到上层 y = 3;
    console.log(x + 3);
  }
*/
fun(5); // 8
fun(7); // 10

案例3

函数的闭包,记住了定义时所在的作用域,这个作用域中的变量不是一成不变的

function outer() {
  var num = 1;
  function inner() {
    return num++;
  }
  return inner;
}
var fun = outer();
/*
  inner的外部环境为
  num = 1;
  fun = function inner() {
    num在该作用域不存在找到上层 num = 1;
    return num++;
  }
*/
console.log(fun());
// 1
console.log(fun());
// 2
console.log(fun());
// 3

案例4

每次调用一个函数,都会产生一个新得闭包,新的闭包,语句全新,外部环境也是新的

function outer() {
  var num = 1;
  function inner() {
    return num++;
  }
  return inner;
}
var fun1 = outer();
var fun2 = outer();
/*
  inner的外部环境为 num = 1;
  fun1 = function inner() {
    num在该作用域不存在找到上层 num = 1;
    return num++;
  }
  fun1 = function inner() {
    num在该作用域不存在找到上层 num = 1;
    return num++;
  }
*/
console.log(fun1());
// 1
console.log(fun1());
// 2
console.log(fun2());
// 1
console.log(fun2());
// 2