07 JSCORE function、作用域和闭包
正课:
1. ***Function
重载
匿名函数
2. *****作用域和作用域链
3. *****闭包
1. ***Function:
什么是: js中一切函数都是对象
函数对象是专门封装函数定义的对象。
创建: 3种:(若为同名函数,后面的会替换前面的)
1. 声明: function 函数名(参数列表){函数体; return 返回值;}
何时: 只要一段代码被反复使用,都要先定义在一个专门的函数中,再反复调用函数即可——复用
何时使用参数: 只要函数步骤中必须某些数据才能正常执行时,就要定义参数。
何时使用返回值: 如果函数的调用者需要函数的执行结果时,函数就必须返回值。
可被声明提前:
****声明提前(hoist): 在开始执行程序前,将所有var声明的变量和function声明的函数提前到*当前作用域*的顶部,集中创建。
*******************赋值留在原地!*****************
2. 函数直接量:
var 函数名=function(参数列表){函数体; return 返回值;};
不会被声明提前。
何时: 只要不希望被声明提前时。
揭示了: 函数名仅是一个普通的变量
函数定义其实是一个对象
函数名中仅保存了函数对象的地址——引用
3. (比较罕见,所有形参必须放在引号中)用new:
var fun=
new Function("参数1","参数2",...,"函数体; return 返回值")
笔试: function compare(a,b){return a-b;}
var compare=function(a,b){return a-b;}
var compare=new Function("a","b","return a-b;");
***重载(overload):
什么是: 相同函数名,不同参数列表的多个函数,在调用时,可根据传入参数的不同,自动选择对应的函数调用!
为什么: 减轻调用者的负担,一个函数名,可执行多种操作
何时: 一项任务,根据不同的参数,执行不同的操作流程时
如何: js语法不支持重载效果!!!!(因为js不允许多个同名函数同时存在,Java标识函数是函数名加列表)
变通: 所有函数对象内,都自动内建了一个arguments对象
arguments对象:
专门保存传入函数的所有参数值的类数组对象
类数组对象: (object like array)即长得像数组的对象
vs 数组: 相同: 下标, length, for遍历
不同: 类数组对象是Object,不是Array,无法使用Array的API
数组是Array类型,可以使用数组类型所有的API
重载必须得用,运用了的有splice(),toString()......
匿名函数:
什么是: 函数创建时,不被任何变量引用的函数
为什么: 节约内存
何时: 如果一个函数只用一次,用完希望自动释放时
如何:2种
1. 回调callback: 将函数作为参数传递给另一个函数去调用
比如: arr.sort(function (a,b){return a-b});
str.replace(/reg/g,function(kw,$1,...){return ...})
2. 自调: 创建函数后立刻调用自己!
何时: 如果一段代码,不希望其中的变量造成全局 的污染,全局变量容易受到篡改,就将其放在匿名函数中
为什么: 建立临时作用域!避免全局污染!
如何:
+function(参数列表){
函数体; return 返回值}();
2. *****作用域和作用域链
作用域scope:
什么是: 一个变量的使用范围——使用
本质上作用域是一个对象——存储
作用域中的变量都是对象的成员
包括:2种
1.全局作用域:(全局变量)随处可用,可反复使用——全局污染
2.函数作用域:(局部变量)仅函数内使用,不可重复使用。
函数的生命周期:
第一阶段:开始执行前:创建执行环境栈(数组):临时保存正在执行的函数的执行环境,向执行环境栈中压入第一个默认函数main();创建全局对象window
第二阶段:定义函数时:创建函数对象,封装函数定义;声明函数名变量,引用函数对象;函数对象的scope属性引用回,创建函数时的作用域;
第三阶段:调用函数时:ECS中压入一个新的元素(执行环境)记录新函数的调用;创建一个活动对象:函数作用域对象,保存本次函数调用用到的局部变量;ECS中的新执行环境元素,引用活动对象,活动中的parent属性引用函数的scope指向的父级作用域对象(即从活动对象指向window:全局作用域对象)。执行过程中优先使用活动对象中的局部对象,若无,就找parent,全局也无,则报错。
第四阶段:函数调用后:执行环境栈中本次函数的执行环境出栈,导致活动对象被释放,
程序/函数的执行过程:
1. 开始执行程序前:
创建ECS(执行环境栈):
依次保存每个调用的函数的执行环境
在ECS中压入第一个全局执行环境(全局EC)
创建window对象,全局EC引用window对象
window就是全局作用域对象
2. 开始执行程序:
所有全局变量都保存在全局作用域对象window中
3. 定义函数时:
在全局添加函数名变量
创建函数对象封装函数定义
函数名变量引用函数对象
函数对象中有一个scope属性,引用回创建函数时的作用域对象。通常都是window。
4. 调用函数时:
在ECS中压入一个新的EC
为本次函数调用创建专门的活动对象(AO)
在AO中创建所有函数定义中规定的局部变量
其实AO就是函数作用域对象
所有局部变量都是AO的成员
新的EC引用活动对象AO
AO的parent指向window
变量的使用顺序:
先用AO(函数作用域)中的局部变量
如果AO中没有,才去window(全局作用域)中找
5. 函数调用后:
本次函数调用的EC出栈
导致函数作用域对象AO释放
导致局部变量一同释放
作用域链(scope chain): 由多个作用域对象连续引用形成的链式结构。掌管着一切变量的使用顺序
顺序: 先函数作用域对象AO->延作用链向父级作用域找
所有的变量都保存在作用域链上的对象中
局部变量都保存在函数作用域对象AO中
全局变量都保存在全局作用域对象window中
控制了: 变量的使用顺序
先用AO(函数作用域)中的局部变量
如果AO中没有,才去window(全局作用域)中找
3.闭包:
什么是: 即重用变量,又防止变量被污染的一种机制
为什么: 全局变量: 优: 可重用 缺: 易被全局污染
局部变量: 优: 不会被污染 缺: 不可重用
何时: 既重用变量,又防止变量被污染
如何: 3步:
1. 用外层函数包裹住受保护的变量和操作变量的内层函数
2. 外层函数将内层函数返回到外部,被外部的变量保存
3. 通过外部变量调用内层函数,访问受保护的变量
缺: 1. 占用更多内存: 外层函数的AO
2. 容易造成内存泄漏
三特点: 1. 函数嵌套:
2. 外层函数包含一个受保护的局部变量
3. 外层函数将内层函数对象返回
有几个闭包就看有几个受保护的变量
闭包如何形成:外层函数的作用域对象(AO)无法释放,即此中outer的AO无法释放
笔试:
1、先找受保护的变量,判断外层函数执行后变量的结果
2、再找操作变量的内层函数:
1、被外层函数return到外部的
2、也可通过直接给全局变量赋值一个函数的方式