JS中函数的基础知识

399 阅读3分钟

函数.jpg

初识函数

函数的意义在于封装

应用:把实现某一个功能的代码封装在一起,后期在想实现这个功能,只需要执行函数即可,不需要重新编写这些代码了。

目的:“低耦合、高内聚”:减少代码的冗余,提高代码使用率。

函数的参数

形参

定义:「变量」,用来接收后期执行时候,传递进来的实参值

  • 生产一个函数,想要实现一些功能,但是实现功能,需要的原材料不确定,需要用户执行它的时候给我,我才知道,此时我们就提供入口 => 形参(变量)

  • 设置形参,但是不传递实参,默认值是undefined

作用:“用来存储执行函数时,传递进来的信息”

实参

定义:执行函数的时候,给指定的入口(形参变量)传递的具体值(值)

function sum(n, m) {
    // n/m:形参「变量」,用来接收后期执行时候,传递进来的实参值
    // 需求:如果n/m的值是非有效数字,我们让其为0
    n = +n;
    m = +m;
    if (isNaN(n)) {
        n = 0;
    }
    isNaN(m) ? m = 0 : null;
    var total = n + m;
    console.log(total);
}
sum(10, '20'); //n=10 m='20'
sum(10); //n=10 m=undefined 设置形参,但是不传递实参,默认值是undefined 

return 返回值

定义:return是函数的返回值机制「出口」:return后面放的是“值”

作用:把变量的值暴露给外面使用 打断函数执行

  • 函数里面return 多少,这个函数的返回值就是多少,如果没有返回值,就是undefined;

  • return会打断函数执行,函数体中遇到return,直接返回结果即可,后面的代码则不再执行了

function sum(n, m) {
    n = +n;
    m = +m;
    isNaN(n) ? n = 0 : null;
    isNaN(m) ? m = 0 : null;
    var total = n + m;
    // console.log(total); //里面用total肯定可以,因为是自己家的「大括号/函数体相当于单独的一家人」
    // return是函数的返回值机制「出口」:return后面放的是“值”,所以此处“return total; -> return 30;”
    //   + 如果不写return,我们默认返回值是undefined
    //   + 函数体中遇到return,直接返回结果即可,后面的代码则不再执行了
    return total;
}
// 外面叫做total,和里面的不是一个变量,没啥直接的关系
//   + sum() : 函数执行,代表的是函数执行后的返回值「RETURN」
//   + sum : 变量/函数名,代表的创建的函数本身「堆内存地址」
var total = sum(10, '20');
console.log(total / 2); //Uncaught ReferenceError: total is not defined 

arguments

arguments.png

定义:arguments函数内置的实参集合

  • 不管我们设置与否形参,再或者是否传递了实参 ARGUMENTS始终都会存在
  • ES6箭头函数中没有ARGUMENTS

形式:arguments 是一个类数组集合

  • 类似数组,但不是数组,和元素集合 HTMLCollection 类似

原理 根据索引记录了每一个传递进来的实参信息

  • 和是否定义形参变量没有关系,ARGUMENTS中包含了所有传递进来的实参信息

  • length属性代表传递实参的个数

任意数求和

//思路:不管传递几个实参值进来,我们都能求出对应的和
//传递的是非有效数字,则按照0处理(或者不处理)
//传递的是字符串,需要先变为数字再求和,避免字符串拼接
function sum() {
    // arguments:内置实参集合「包含了传递进来的所有实参值 “类数组集合”」
    var total = 0;
    for (var i = 0; i < arguments.length; i++) {
        // 获取当前循环的集合中的这一项,并且把其变为数字
        var item = +arguments[i];
        // 如果item不是一个有效数字,则结束当前这轮循环「不求和了」,继续下一轮即可
        if (isNaN(item)) continue;
        total += item;
    }
    return total;
}
console.log(sum(10, 20, '30', 'AA', 40));//100
console.log(sum(10, 20, 30));//60
console.log(sum(10));//10
console.log(sum()); //0

创建函数和执行函数在堆栈内存中的逻辑

  • E(execution)C(context)Stack:执行环境栈,又名“栈内存”
  • EC:私有的上下文
  • EG:全局执行上下文(全局代码都在这里执行)
  • AO:私有变量对象
  • VO:全局变量对象(全局上下文声明的变量都在这里)

创建执行函数.png

创建函数

语法:function 函数名(形参){ 函数体 }

创建过程

  • 第一步:创建值:
    • 1、开辟一个堆内存
    • 2、把函数体中的代码当作字符串储存在堆中
    • 3、把堆地址放到栈中
  • 第二步:创建变量
  • 第三步:让变量和地址关联 只创建函数,但不执行,函数没有任何意义,因为它只是开了堆内存,存储了一堆字符串而已

执行函数

语法:函数名(实参)

目的:把创建的函数执行(把函数体中的代码执行)

执行过程

  • 形成一个全新的私有上下文,并且进栈执行
  • 在私有上下文中,也有一个存放自己变量和值的地方:AO 私有变量对象
  • 把之前存放在堆内存中的代码,字符串拿出来执行
    • 执行代码之前,还有很多很多步骤...eg:this,变量提升
  • 当上下文中的代码都执行完后,如果该上下文中的信息没有被外界占用的情况,则执行完出栈(释放掉)以减少栈内存中的空间;

函数的表达形式

实名函数:有函数名的

匿名函数:没有名字的函数

函数表达式:把一个函数作为值,赋值给一个变量或者一个事件绑定等

var sum = function () {};
box.onclick = function () {};

自执行函数:创建和执行都一起完成的

用小括号包起来,或者前面加“~/+/!”等都是为了保证语法不出错

  • (function(n){...})(实参)
    • function(n){...}(实参)
    • function(n){...}(实参)
  • ~ function(n){...}(实参)
  • ! function(n){...}(实参)
var res = (function (i) {
    // ...函数体
    return 100;
})(10); */
~function(){}();
!function(){}();
+function(){}();

回调函数:把一个函数作为“实参「值」”传递给另外一个函数

function fn(callback) {
    // callback : 传递进来的匿名函数
    callback();
}
fn(function () {}); 

var arr = [11, 2, 5, 34, 24];
// sort是一个内置函数,此处是把sort执行
arr.sort(function (a, b) {
    return a - b;
});
把数组的forEach方法执行,传递一个匿名函数A「回调函数」,那么在forEach内部,就可以获取到这个A
接下来forEach内部干了点事情:循环数组中的每一项,每循环一次都把A执行一次「数组有多少项,回到函数A就要被执行多少次」,并且把每次循环得到的当前项和当前项索引,传递给A的item/index...
-> A : function(item,index){}
var arr = [11, 2, 5, 34, 24];
arr.forEach(function (item, index) {
    // item:当前项
    // index:当前项的索引
}); 

箭头函数

与普通函数的区别:

  • 没有arguments
  • 没有自己的this,this是执行上下文的this
let fn = () => {};
//如果箭头函数只有一个参数,可省略小括号,只写形参
let fn2 = str => {};
//如果大括号内,除了return没有多余语句,可省略大括号和return
let fn3 = num => num + 1;
// 写全=>let fn3 = (num) => {return num + 1};