• 函数是指一段在一起的、可以做某一件事儿的程序。也叫做子程序、(OOP中)方法
  • 函数实现某一个功能的方法

创建函数

1
2
3
4
functoin [函数名](){
// => [函数体]
// 实现功能的具体js代码
}

执行函数

1
2
函数名(); // 创建的函数执行,而且这个函数可以执行很多次
函数名();

每一次执行都相当于把函数体重实现功能的js代码重复执行了一遍

在真实的项目中,我们一般都会把实现一个具体功能的代码封装到函数中

  • 如果当前这个功能需要在页面中执行多次,不封装成为函数,每一次实现这个功能,都需要重新吧代码写一遍,浪费时间;而封装在一个函数中,以后想实现多次这个功能,我们就没有必要在重新写代码,只需要把函数重新的执行即可,提高了开发效率
  • 封装在一个函数,页面中就基本上很难重复一样的代码了,减少了页面中代码的冗余度,提高了代码的重复利用率: 低耦合高内聚

我们把以上的特点成为函数封装 (OOP面向对象编程思想,需要我们掌握的就是类的继承、封装、多态)

JS中函数的核心原理

函数作为js中引用数据类型中的一种,也是按照引用地址操作的

1
2
3
4
5
6
function sum(){
var total = 1+1;
total *= 20;
console.log(total.toFixed(2));
}
sum();

创建函数

  • 首先会在当前作用中声明一个函数名(声明的函数和使用var声明变量是一样的操作:var sum;function cum;这两个名字算重复了)
  • 浏览器首先会开辟一个新的内存空间(奉陪一个16进制地址),把函数体重写好的代码当做普通字符串存储在这个内存空间(创建一个函数如果不执行,函数没有意义)
  • 把内存空间的地址赋值给之前声明的那个函数名

函数执行

  • 目的:把之前存储的实现具体功能的js代码执行
  • 函数执行,浏览器首先会为其开辟新的私有作用域(只能执行函数中之前编写的js代码)
  • 形参赋值
  • 私有作用中的变量升级
  • 把之前穿件时间存储的那些js代码字符串,拿到自由作用域中,然后把题目变成js表达式从上到下执行
  • 私有作用域是否销毁的问题

![image.png](https://cdn.nlark.com/yuque/0/2019/png/271124/1553076800854-5e22d4eb-aa50-40a1-a8de-6abbd5cf8939.png#align=left&display=inline&height=203&name=image.png&originHeight=406&originWidth=986&size=115231&status=done&width=493)

闭包

函数执行会形成一个私有的作用域,让里面的私有变量和外界互不影响(相互干扰、外面的无法直接获取里面的变量值),此时我们可以理解为私有作用域把私有变量保护起来,我们把这种保护机制称为为闭包

栈内存

作用域(全局作用域/私有作用域):提供一个供js代码执行的环境

堆内存

所有的引用数据类型,他们需要存储的内容都是堆内存中(相当于一个仓库,目的是存储信息)

  • 对象会吧键值队存储起来
  • 函数会把代码当做字符串存储起来

函数中形参和实参

  • 形参:相当于生成洗衣机的时候提供的入口,需要用户执行函数的时候把需要的值传递进来,形参是个变量,用来春初和接口那些值
  • 实参:用户执行的时候传递给形参的具体指
1
2
3
4
5
6
7
8
9
10
11
// 随便求出两个数的和

function sum(num1,num2){ //num1/num2就是形参变量(类似于var了一下)
var total = num1 + num2;
total*=10;
total=total.toFixed(2);
console.log(total);
}

sum(10,20);//10/20是实参 num1=10 num2=20
sum(10); // num1=10 num2=undefined 定义了形参但是执行的时候,没有传递实参,默认实参就是undefined

arguments实参集合

当我们不知道用户具体要传递几个值的时候(传递几个值都行),此时我们无法设置形参的个数:遇到此类需要,需要使用函数内置的实参集合:arguments

  • argument 只有函数才有
  • 不管执行函数的时候是否传递实参,arguments天生就纯在,没有传递实参ARG是个空的集合传递了ARG中包含了所有传递的实参值
  • 不管是否设置了形参,ARG中始终存储了所有的实参信息
1
2
3
4
function sum(){
console.log(arguments)
}
sum(10,20,'wjh',{name:'wjw'});

image.png

  • arguments 是个类数组集合
    • 以数字作为索引(属性名),从0开始
    • arguments[0] 第一个实参信息
    • arguments[2] 第三个实参信息
    • arguments[n] 第n+1个实参信息
  • 有一个length的属性,存储的是当前几个的长度(当前传递实参的个数)
    • arguments.length
    • arguments[‘length’]
    • arguments.calle 存储的是当前函数本身
    • arguments.calle.caller 存储的是当前函数只在哪执行的(宿主函数),在全局作用域下执行的,结果是null
1
2
3
4
5
6
7
function sum(){
console.log(arguments.callee.caller);//f
}
function fn(){
sum(10,20,'wjh',{name:'wjw'});
}
fn();

// arguments.call或者arguments.call.caller一般真正项目中很少使用,因为是在严格js模式下不允许我们直接使用这两个属性,然而现有项目大部分都是基于严格模式来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 任意数求和

function sum(){
var total = null;
for(var i =0;i<arguments.length;i++){
var cur = Number(arguments[i]);
!isNaN(cur)?total += cur : null
}
consloe.log(total);
return total;
// return 后面跟着的都是值(返回的都是值):此处不少TOTAL变量返回,而是吧total存储到值返回而已
// return 60;
}
sum(10,20,20);
sum();
sum(10,20,30,'wjw')
// console.log(total);
//=>Uncaught ReferenceError: total is not defined 闭包的保护机制导致作用域会保护里面的私有变量

JS中的返回值return

返回值是函数提供的一个出口:我们如果想在外面使用函数私有的一些信息,那么就需要通过return,把这些信息返回出来供外面使用

sum:代表的是函数本身
sum() 让函数先执行,代表的是当前函数返回的结果(return)后面是啥,相当于函数返回的是啥

1
2
3
4
5
6
function sum(){
var total = 0;
renturn
}
console.log(sum());
// 如果函数中没有return或者return后面啥也没有,默认返回的结果是undefined
1
2
3
4
5
6
function sum(){
var total = 0;
renturn;
console.log(sum());
// 函数体重遇到return后,return后面的代码都不在执行了
}

js中匿名函数

没有名字的函数

  • 函数表达式
  • 自执行函数
1
2
3
4
oBox.onclick = function(){
// 把一个码云名字的函数(有名字的也无所谓)作为值赋值给一个变量或者一个元素的某一个事件等,函数表达式

}
1
2
3
4
5
6
7
8
9
10
(function(n){
// 创建函数和执行函数放在一起,穿件完成立马之执行:自执行函数
// n 形参 n=10
})(10)

// 以下都是自执行函数,符号只有控制语法规范
~function(){}(10)
-function(){}(10)
+function(){}(10)
!function(){}(10)