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

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

return 返回值

return会直接返回值 ;return会打断函数执行

  1. function sum(x, y) {
  2. return x + y;
  3. console.log(123);
  4. }
  5. sum(1,2);//返回值为3,但是不会输出123

arguments实参合集

arguments函数内置的实参集合:不管我们设置与否形参,再或者是否传递了实参

ARGUMENTS始终都会存在(ES6箭头函数中没有ARGUMENTS

arguments是一个类数组集合(类似数组,但是不是数组)

根据索引记录了每一个传递进来的实参信息(和是否定义形参变量没有关系,ARGUMENTS中包含了所有传递进来的实参信息)

length属性代表传递实参的个数
1618021455187-c73c794d-9b9c-4a8e-b916-46f7933195b3.png
arguments 是函数获得到所有参数集合,下面是使用 arguments 求和的例子

  1. function sum() {
  2. return [...arguments].reduce((total, num) => {
  3. return (total += num);
  4. }, 0);
  5. }
  6. console.log(sum(2, 3, 4, 2, 6)); //17

更建议使用展示语法

  1. function sum(...args) {
  2. return args.reduce((a, b) => a + b);
  3. }
  4. console.log(sum(2, 3, 4, 2, 6)); //17

函数创建的具体过程

  1. 创建值
  2. 开辟一个堆内存
  3. 把函数体中的代码当做字符串存储到堆中
  4. 把堆地址放到栈中
  5. 创建变量
  6. 让变量和地址关联

注:创建函数,其实就是创建了一个储存了一堆字符串的堆而已,并没有实际作用。

5.1 - 函数 - 图3

函数执行的具体过程

执行过程

  1. 形成一个私有的执行上下文(AO),然后进栈执行
  2. 初始化作用域链
  3. 初始化THIS
  4. 初始化ARGUMENTS
  5. 形参赋值
  6. 变量提升
  7. 代码执行
  8. 根据情况决定是否出栈释放

作用域

创建函数的时候就声明了它的作用域,在哪个上下文中创建的,那它作用域就是哪个上下文

作用域和上下文是同一意思

是,都是函数执行形成的那个空间,FN的作用域【ECG】ECG 也是全局上下文 从核心来说 都是栈内存

函数自己执行形成的EC(FN) 上下文

函数创建的时候声明了它的作用域

函数执行会形成一个”全新”的”私有”的执行上下文 然后进栈执行

一般情况下,函数执行完,形成的这个私有上下文会出栈释放 来优化内存空间

EC(FN) 私有执行上下文

AO(FN) 私有变量对象 [私有上下文声明的变量都存储在这里=>私有变量]

  • AO[active object]是VO的分支,函数私有上下文中[AO]
  • 私有变量(AO):
  • @1 形参变量
  • @2 函数体中声明过的变量

代码执行前的步骤

  1. 初始作用域链:scope chain
  2. 初始化THIS
  3. 初始ARGUMENTS—实参集合
  4. 形参赋值
  5. 变量提升

作用域链

<自己的上下文【执行产生的】,函数的作用域【创建时候声明的】>

作用域链机制

  1. 私有上下文中,变量,首先看是否为自己上下文中的私有变量,如果是自己的【AO】,则接下来所有操作,都是操作自己的
  2. 如果不是自己私有的变量,则按照作用域链,查找是否为其上级上下文中变量【上级上下文就是函数的作用域】如果找到了,则后期操作的都是上级上下文中的变量
  3. 如果上级上下文也没有这个变量,则继续找其”上上级”上下文,一直到EC(G)全局上下文位置
  • 如果全局上下文中也没有
  • 获取变量值就是报错
  • 设置变量值就是给window设置的属性

作用域链只向上查找,找到全局window即终止,应该尽量不要在全局作用域中添加变量。

匿名函数image-20191007192620939.4ff7f251.png

没有名字的函数

1.匿名函数的第一种情形【事件绑定】

  1. var btn=document.querySelector("#btn");
  2. btn.οnclick=function(){
  3. // alert("document");
  4. }

2匿名函数的第二种情形【定时器】

  1. //定时器
  2. //setTimeOut(函数体,时间) 多少毫秒之后执行(只执行一次)
  3. //setInerval(函数体,时间) 每隔多少毫秒一次
  4. //清定时器用clearInterval(timer);
  5. setTimeout(function(){
  6. console.log("只执行一次呀");
  7. },1000);
  8. var timer=setInterval(function(){
  9. console.log("每隔两秒执行一次");
  10. },2000);
  11. //10秒停止 清空定时器
  12. setTimeout(function(){
  13. clearInterval(timer);
  14. },10000);

3. 匿名函数的第三种情形【匿名函数具名化】

  1. var fn = function () {};
  2. fn();

4. 匿名函数的第四种情形【作为对象中的一个属性】

  1. var obj={
  2. name:"dddd",
  3. say:function(){
  4. alert(this.name);
  5. }
  6. }
  7. obj.say();

箭头函数

与普通函数的区别:

1.没有arguments

2.没有自己的this,this是执行上下文的this

  1. let fn = () => {};
  2. //如果箭头函数只有一个参数,可省略小括号,只写形参
  3. let fn2 = str => {};
  4. //如果大括号内,除了return没有多余语句,可省略大括号和return
  5. let fn3 = num => num + 1;
  6. // 写全=>let fn3 = (num) => {return num + 1};

自执行(匿名)函数

一个函数在定义后立即执行

  1. let a = (function () {
  2. console.log(123);
  3. //可以拿到返回值
  4. return 1;
  5. })();
  6. let b = ~ function () {
  7. console.log(123);
  8. //拿不到返回值
  9. return 2;
  10. }();
  11. console.log(a, b);//=>1 -3因为2按位取反的值就是-3

回调函数

  • 把一个函数作为[实参‘值’]传递给另外一个函数
  1. function fn(callback){
  2. callback();
  3. }
  4. fn (function (){});

递归

函数自己调用自己,叫做递归

边界条件:用来停止递归的,如果没有边界条件,则会成为死循环

  1. //求1-100的累加结果
  2. function sum(n) {
  3. //边界:停止条件
  4. if(n<0) {
  5. return 0
  6. }
  7. return n+=sum(n-1);
  8. }
  9. sum(100)