函数的意义在于封装:把实现某一个功能的代码封装在一起,后期在想实现这个功能,只需要执行函数即可,不需要重新编写这些代码了。
“低耦合、高内聚”:减少代码的冗余,提高代码使用率。
return 返回值
return会直接返回值 ;return会打断函数执行
function sum(x, y) {
return x + y;
console.log(123);
}
sum(1,2);//返回值为3,但是不会输出123
arguments实参合集
arguments函数内置的实参集合:不管我们设置与否形参,再或者是否传递了实参
ARGUMENTS始终都会存在(ES6箭头函数中没有ARGUMENTS)
arguments是一个类数组集合(类似数组,但是不是数组)
根据索引记录了每一个传递进来的实参信息(和是否定义形参变量没有关系,ARGUMENTS中包含了所有传递进来的实参信息)
length属性代表传递实参的个数
arguments 是函数获得到所有参数集合,下面是使用 arguments
求和的例子
function sum() {
return [...arguments].reduce((total, num) => {
return (total += num);
}, 0);
}
console.log(sum(2, 3, 4, 2, 6)); //17
更建议使用展示语法
function sum(...args) {
return args.reduce((a, b) => a + b);
}
console.log(sum(2, 3, 4, 2, 6)); //17
函数创建的具体过程
- 创建值
- 开辟一个堆内存
- 把函数体中的代码当做字符串存储到堆中
- 把堆地址放到栈中
- 创建变量
- 让变量和地址关联
注:创建函数,其实就是创建了一个储存了一堆字符串的堆而已,并没有实际作用。
函数执行的具体过程
执行过程
- 形成一个私有的执行上下文(AO),然后进栈执行
- 初始化作用域链
- 初始化THIS
- 初始化ARGUMENTS
- 形参赋值
- 变量提升
- 代码执行
- 根据情况决定是否出栈释放
作用域
创建函数的时候就声明了它的作用域,在哪个上下文中创建的,那它作用域就是哪个上下文
作用域和上下文是同一意思
是,都是函数执行形成的那个空间,FN的作用域【ECG】ECG 也是全局上下文 从核心来说 都是栈内存
函数自己执行形成的EC(FN) 上下文
函数创建的时候声明了它的作用域
函数执行会形成一个”全新”的”私有”的执行上下文 然后进栈执行
一般情况下,函数执行完,形成的这个私有上下文会出栈释放 来优化内存空间
EC(FN) 私有执行上下文
AO(FN) 私有变量对象 [私有上下文声明的变量都存储在这里=>私有变量]
- AO[active object]是VO的分支,函数私有上下文中[AO]
- 私有变量(AO):
- @1 形参变量
- @2 函数体中声明过的变量
代码执行前的步骤
- 初始作用域链:scope chain
- 初始化THIS
- 初始ARGUMENTS—实参集合
- 形参赋值
- 变量提升
作用域链
<自己的上下文【执行产生的】,函数的作用域【创建时候声明的】>
作用域链机制
- 私有上下文中,变量,首先看是否为自己上下文中的私有变量,如果是自己的【AO】,则接下来所有操作,都是操作自己的
- 如果不是自己私有的变量,则按照作用域链,查找是否为其上级上下文中变量【上级上下文就是函数的作用域】如果找到了,则后期操作的都是上级上下文中的变量
- 如果上级上下文也没有这个变量,则继续找其”上上级”上下文,一直到EC(G)全局上下文位置
- 如果全局上下文中也没有
- 获取变量值就是报错
- 设置变量值就是给window设置的属性
作用域链只向上查找,找到全局window即终止,应该尽量不要在全局作用域中添加变量。
匿名函数
没有名字的函数
1.匿名函数的第一种情形【事件绑定】
var btn=document.querySelector("#btn");
btn.οnclick=function(){
// alert("document");
}
2匿名函数的第二种情形【定时器】
//定时器
//setTimeOut(函数体,时间) 多少毫秒之后执行(只执行一次)
//setInerval(函数体,时间) 每隔多少毫秒一次
//清定时器用clearInterval(timer);
setTimeout(function(){
console.log("只执行一次呀");
},1000);
var timer=setInterval(function(){
console.log("每隔两秒执行一次");
},2000);
//10秒停止 清空定时器
setTimeout(function(){
clearInterval(timer);
},10000);
3. 匿名函数的第三种情形【匿名函数具名化】
var fn = function () {};
fn();
4. 匿名函数的第四种情形【作为对象中的一个属性】
var obj={
name:"dddd",
say:function(){
alert(this.name);
}
}
obj.say();
箭头函数
与普通函数的区别:
1.没有arguments
2.没有自己的this,this是执行上下文的this
let fn = () => {};
//如果箭头函数只有一个参数,可省略小括号,只写形参
let fn2 = str => {};
//如果大括号内,除了return没有多余语句,可省略大括号和return
let fn3 = num => num + 1;
// 写全=>let fn3 = (num) => {return num + 1};
自执行(匿名)函数
一个函数在定义后立即执行
let a = (function () {
console.log(123);
//可以拿到返回值
return 1;
})();
let b = ~ function () {
console.log(123);
//拿不到返回值
return 2;
}();
console.log(a, b);//=>1 -3因为2按位取反的值就是-3
回调函数
- 把一个函数作为[实参‘值’]传递给另外一个函数
function fn(callback){
callback();
}
fn (function (){});
递归
函数自己调用自己,叫做递归
边界条件:用来停止递归的,如果没有边界条件,则会成为死循环
//求1-100的累加结果
function sum(n) {
//边界:停止条件
if(n<0) {
return 0
}
return n+=sum(n-1);
}
sum(100)