Function、闭包
1.函数
1.函数声明
function 函数名(参数列表){函数体;return返回值}
注意:函数里的变量会声明提前
2.赋值
let 函数名=function(参数列表){函数体;return返回值}
优点:函数里的变量不会声明提前
3.用new
let 函数名=new Function("参数1","参数2","…","函数体";)
此方法不推荐使用
2.重载
(1)定义:相同函数名,不同参数列表的多个函数,在调用时,可根据传入参数的不同,自动选择对应的函数执行
(2)优点:减少API的数量,减轻调用者的负担
(3)语法:js语法默认不支持重载,因为 js不允许多个同名函数同时存在,最后创建的会覆盖之前创建的所有
解决:arguments
arguments:
函数调用时自动创建的 接收所有传入函数的参数值的 类数组对象
类数组对象:长的像数组的对象,跟数组比较,类型不同,API不通用
练习1:使用重载,定义一个函数,当不输入数字是在控制台输出“不合格”,当输入一个数字时输出“合格”,当输入两个数字时输出“优秀”
function pay(){ switch(arguments.length){ case 0:console.log("手机支付"); break; case 1:console.log("现金支付"); break; case 2:console.log("刷卡支付"); break; } } pay(); pay(100); pay("123456","654321");
练习2:利用函数的重载,计算一串数字的和
function add(){ let sum=0; for(let i=0;i<arguments.length;i++){ sum+=arguments[i]; } return sum; } console.log(add(1,2,3));
3.匿名函数
1.优点:节约内存、划分临时作用域
2.使用场景:一个函数使用后,希望立即释放的
3.回调(callback):将一个函数,交给另一个函数去自动调用
比如:arr.sort(function(a,b){return a-b;})
xhr.onreadystatechange=function(resText){}
btn.onclick=function(){}
str.replace(/正则/,function(kw){return xxx})
4.自调:定义函数后,立即调用自己
使用场景:几乎所有自定义的脚本,都要放在匿名函数中
优点:避免使用全局变量,避免全局污染
语法: (function(…){…})()
4.作用域(scope)和作用域链
(1)用途:一个变量的可用范围,其实是一个保存变量的对象,为了避免不同范围的变量间互相干扰
(2)包括
1. 全局作用域: window
保存全局变量: 优: 可重复使用,随处可用
缺: 全局污染
2. 函数作用域: 临时创建的活动对象AO
保存局部变量: 优: 不污染全局
缺: 不可重用,仅函数内可用
(3)原理
1. 程序开始执行前:
在内存中创建执行环境栈(数组):
执行环境栈: 依次保存正在调用的函数的数组
函数调用后,会出栈
在栈中首先添加浏览器主函数的调用
创建全局作用域对象window
主函数引用全局作用域对象window

2. 定义函数时:
在window中用函数名创建变量
在window外创建函数对象保存函数的内容
函数名变量通过地址引用函数对象
函数对象用隐藏的scope属性,引用回自己诞生的作用域对象。——万一运行时缺东西,可以找诞生的作用域要!

3. 函数调用时:
在执行环境栈中添加当前函数调用
为本次函数调用创建活动对象AO:
活动对象: 专门保存本次函数调用所需的局部变量的对象——就是函数作用域对象
在活动对象中保存函数的局部变量
活动对象通过隐藏的parent属性引用window
变量的使用顺序: 先用AO中的局部变量,局部没有,才去window找。

4. 函数调用后:
函数的调用从ECS中出栈
导致: 函数作用域对象AO释放
导致: 局部变量一同释放!

(4)作用域链: 由多级作用域对象,逐级引用形成的链式结构
作用: 存储所有变量(局部变量和全局变量)
控制着变量的使用顺序,先局部再全局

5.闭包
1.作用:重用一个变量,又保护变量不被篡改的一种机制
2. 全局变量:优点:可重用
缺点:造成全局污染
局部变量:优点:仅函数内可用,不会污染全局,也不会被篡改
缺点:不可重用
3.闭包形成原因:外层函数的函数作用域对象无法释放
4.过程
1.用外层函数包裹,要保护的变量和使用变量的内层函数
2.外层函数返回内层函数
3.调用者,调用外层函数,获得内层函数对象
缺点:闭包比普通函数占用更多内存
解决:用完后立刻释放
5.注意:
1. 先找受保护的变量和内层函数,画简图:
2. 同一次外层函数调用,返回的多个内层函数,共享同一个闭包变量
3. 多次外层函数调用,会创建多个闭包。多个闭包之间彼此毫无关系!
练习1:在控制台从1开始打印
//闭包 function outer(){ let i=1; return function(){ console.log(i++); } } let getNum=outer(); //getNum:function(){ console.log(i++); } getNum(); getNum(); getNum(); getNum(); getNum(); getNum(); //释放闭包 getNum=null;
练习2:在页面中设置一个按钮显示“赞+0”,每次点击一次按钮,赞就+1
<button id=btnZan>赞(+0)</button>
<script>
(function () {
let n = 0;
btnZan.onclick = function () {
btnZan.innerHTML = "赞(+" + (++n) + ")";
}
})()
</script>
练习3:在页面设置一个点赞按钮和一个点踩按钮,每次点击都会+1
<button id=btnZan>赞(+0)</button>
<button id=btnCai>踩(+0)</button>
<script>
function outer(id,tite) {
let n = 0;
return function () {
id.innerHTML = tite+"(+" + (++n) + ")";
}
}
btnZan.onclick=outer(btnZan,"赞");
btnCai.onclick=outer(btnCai,"踩");
</script>

浙公网安备 33010602011771号