1. 函数 2. 重载 3. 匿名函数 4. 作用域和作用域链 5. ***闭包
一. 回顾函数:
1. 什么是: 保存一段可重用的代码段的程序结构,再起一个名字
2. 为什么: 重用,避免重复编码
3. 何时: 只要一段代码,可能被反复使用时,都必须先封装在函数中,再反复调用函数——DRY:Don't repeat yourself
4. 如何创建函数: 3种:
(1). 用声明方式创建:
function 函数名(形参列表){
函数体;
return 返回值
}
问题: 会被声明提前——破坏程序原有的执行顺序
声明提前: hoist(起重机)
什么是: 在程序开始执行前,
Js引擎会先将var声明的变量和function声明的函数,提前到当前作用域的顶部集中创建
赋值(=)留在原地!
(2). 用赋值方式创建:
var函数名=function (形参列表){
函数体;
return 返回值
}
优点: 仅=前的声明部分被提前,而函数定义留在原地。
(3). 用new方式创建: ——了解!
var 函数名=new Function("形参1","形参2","函数体; return 返回值")
函数其实也是一个对象,只不过内部存的不是数据,而是代码段而已。
函数名仅仅是一个普通的变量。函数名通过保存函数对象的地址,来引用这函数对象。
当我们调用函数名时,其实是让引擎找到函数名变量应用的函数对象,按照函数对象中保存的代码段,执行程序。
function其实是new Function的简写!所以程序每指定一次function,都等效于一个new Function,都会创建一个新函数对象!
5. 形参:
(1). 什么是: 专门接受函数执行时必须的参数值得变量
(2). 为什么: 有些函数必须提供某些数据才能正常执行
(3). 何时: 只要一个函数,自己缺少一些数据,需要外部提供数据,才能正常执行时!就要定义形参!
(4). 强调: 形参虽然没有var, 但是也属于局部变量!
6. 返回值:
(1). 什么是: 一个函数的执行结果
(2). 为什么: 调用者需要获得函数的执行结果
(3). 何时: 如果一个函数的调用者希望获得函数的执行结果,进行后续操作时,函数中就必须定义返回值
(4). 如何: return 返回值;
(5). 强调: (存在的坑)
a. return 其实可以单用!表示退出函数的意思!
vs break:只退出循环
b. return后不要随意换行,一旦return后换行,则js引擎会自动在return后加 ; 换行之后的代码不再执行!
function add(a,b){
return a+b;
} //正确
function add(a,b){
return ; //js引擎会自动在return后加;
a+b;
} //错误: 返回undefined
二. 重载(overload):
1. 什么是: 多个同名的函数,但是形参列表不同,在调用时,可自动根据传入实参列表的不同,选择匹配的函数执行。
2. 为什么: 减少函数的个数,减轻调用者的负担。
3. 何时: 今后只要一件事!传入不同的实参列表,希望执行不同的操作时,就必须用重载!
4. 如何:
(1). 问题: js不支持其他语言那种重载的语法:
a. 因为js不允许多个同名函数同时存在!
b. 如果同时定义多个同名函数,则最后一个同名函数,会覆盖之前所有同名函数!
(2). 解决: 借助于arguments对象变通实现
a. 只定义一个函数! 且不要定义形参!
b. 每个函数内都有一个arguments对象:
1). 什么是arguments: 专门接受传入函数的所有实参值得类数组对象
2). 什么是类数组对象: 长得像数组的对象
i. 相同: 下标, length
ii. 不同: 不是数组类型,而是对象类型,所以类数组对象无法使用数组家的函数!
3). 何时使用: 今后在js中,如果一个函数的实参值个数不确定,就必须用arguments代替形参列表!
4). 如何使用:
i. 不用自己创建!可直接使用!
ii. 获得实参值: arguments[i]
iii. 获得实参个数: arguments.length
c. 在要实现重载的函数内,判断arguments中的实参值个数或实参内容不同,来动态选择不同的逻辑执行!
5. 原生函数中重载的例子: arr.splice()
(1). 删除元素: arr.splice(i, 几个)
(2). 插入新元素: arr.splice(i, 0, 新值1, 新值2,...)
(3). 替换现有元素: arr.splice(i, 几个, 新值1, 新值2,...)
三. 匿名函数:
1. 什么是: 创建函数时,不指定函数名的函数
2. 为什么: 节约内存!因为用完就自动释放!
3. 何时: 今后只要一个函数只使用一次时,都必须用匿名函数
4. 如何:
(1). 回调函数:
a. 回调函数几乎都是匿名函数
b. 因为回调函数几乎都是为主函数量身定制的,别的函数也用不了。所以,希望回调函数用完,和主函数一起释放!
c. 比如:
1). arr.sort(function(a,b){return a-b})
2). str=str.replace(/正则/ig, function(kw){ return xxxx })
(2). 匿名函数自调:
a. 创建一个匿名函数后,立刻调用自己!
b. 何时: 今后所有js代码都应该包裹在一个匿名函数自调内!
c. 为什么: 避免使用全局变量!避免全局污染
d. 如何:
(1). 标准:
(function(形参列表){
函数体;
return 返回值
})(实参值列表)
(2). 杀马特写法:
+function(){...}()
!function(){...}()
e. 坑!匿名函数前后的分号不要省略!
错误: (function(){ ... })()
(function(){ ... })()
报错: (intermediate value)(intermediate value)(...) is not a function
正确: (function(){ ... })();
(function(){ ... })();
四. 作用域(scope)和作用域链:
1. 什么是作用域:
(1). 作用: 一个变量的可用范围
(2). 本质: 一个保存变量的对象
2. 为什么: 为了避免不同范围内的数据之间互相干扰!
3. 包括: 2级:
(1). 全局作用域: window对象
a. 专门保存所有全局变量的作用域
b. 优点: 随处可用,可反复使用!
c. 缺点: 极易被污染
(2). 函数作用域: ?对象
a. 专门保存仅函数内可用的局部变量的作用域
b. 优点: 因为仅函数内可用,所以不会被污染
c. 缺点: 不可重用!
4. 函数的生命周期: 一个函数从创建,到调用,最后到调用完所经历的阶段
(1). 定义函数时:
a. 创建函数对象
b. 创建函数名变量,保存函数对象的地址
c. 每个函数其实都有一个"好友列表"。一般函数的好友列表中只包含两个格:
1). 离函数近的这个格: 暂时空着
2). 离函数稍远的格: 存window的引用
为什么: 因为每个函数都害怕将来执行时,自己少东西,不知道该找谁帮忙!好友列表中保存window,是为了,万一将案例执行时,自己缺少变量,还可找window要!
(2). 调用函数时:
a. 临时创建本次函数调用的函数作用域对象
b. 在函数作用域对象中创建本次函数所需的所有局部变量
包含两种情况:
1). 在函数内var中的变量
2). 形参变量虽然没有var,但是也属于局部变量!
c. 函数要有列表中离函数近的一个格,会立刻引用函数作用域对象
d. 函数执行过程中,变量的使用顺序:
1). 先局部
2). 局部没有,才全局
(3). 函数调用后:
a. 释放函数作用域对象
b. 导致函数作用域对象中的局部变量也跟着释放!
c. 所以,所有局部变量都不可重用!
5. 作用域链:
(1). 什么是: 由多级作用域串联形成的链式结构
(2). 每个函数在创建时,就有了自己的作用域链。普通函数作用域链里包含2个格:
离自己近的格,暂时为空,调用函数时,用来临时引用函数作用域对象
离自己远的格,始终保存着全局作用域对象window
(3). 保存着一个函数可用的所有变量
(4). 控制着变量的使用顺序: 先局部,局部没有,才全局!
6. js中没有块级作用域:
(1). 程序块: 除函数以外的循环和分支的{},称为程序块
比如: for(var i=0;i<arr.length;i++){
...
}
比如: if(xxx){
...
}else{
...
}
(2). 在Java语言中循环和分支结构的{}也是一级作用域,循环和分支结构内var的变量,出了{},不能使用!
比如: if(xxx){
var a=10;
}else{
var a=100;
}
console.log(a) //错误!
改为:
var a;
if(xxx){
a=10;
}else{
a=100;
}
console.log(a) //正确!
(3). 但是,js中循环和分支的{}不是作用域,即使在循环内和分支内声明的变量,出了循环和分支,照样使用!
比如: if(xxx){
var a=10;
}else{
var a=100;
}
console.log(a) //正确!
五. 闭包(closure):
1. 什么是闭包:
(1). 使用: 既重用一个变量,又保护变量不被篡改的一种编程方法
(2). 本质: 外层函数的函数作用域对象,因为被内层函数引用着无法释放,就成了闭包!
2. 为什么: 全局变量和局部变量都有不可兼得的优缺点:
(1). 全局变量:
a. 优点: 可重用
b. 缺点: 极易被污染
(2). 局部变量:
a. 优点: 不会污染
b. 缺点: 不可重用
3. 何时: 只要希望反复使用一个变量,又保护这个变量不被别人篡改时
4. 如何: 3步,三特点:
(1). 用外层函数包裹要保护的变量和内层函数对象
(2). 外层函数必须返回内层函数对象到外部
(3). 使用者必须调用外层函数才能获得返回的内层函数
结果: 反复调用返回的内层函数,可以反复使用外层函数的局部变量,且不会被篡改!
5. 闭包形成的原因: 外层函数调用后,外层函数的作用域对象无法释放,形成了闭包!(因为被内层函数对象的作用域链引用着)
6. 闭包的缺点: 比一般的函数多占用内存!多占用的是外层函数的函数作用域对象
7. 如何解决: 主动释放!
保存内层函数的全局变量=null
结果: 先释放内层函数对象,再释放外层函数的函数作用域对象
8. 鄙视时: 画简图:
(1). 找受保护的变量是谁?——通常是妈妈的局部变量
(2). 找妈妈生出了几个内层函数孩子:
a. 孩子可以通过return返回到外部!
b. 强行给全局变量赋值一个内层函数
c. 包裹在一个对象或数组中同时返回出多个内层函数.
结论: 妈妈一次生出的多个孩子,共用同一个变量!
JS高级(day03)
最新推荐文章于 2024-08-13 21:01:07 发布