为什么了解AO GO:为了解决作用域、作用域链相关所产生的一切问题 AO -> function 相当于独立的仓
库 互相不影响
(1)作用域
对象
函数也是一种对象类型、引用类型、引用值
function test(a, b){}
可以访问的属性 test.length test.name test.protoytpe 注 函数名.length 是指形参的长度
- 对象 -> 有些属性使我们无法访问的
Js引擎内部固有的隐式属性
2. [[scope]] 域
1. 函数创建时,生成的的一个JS内部的隐式属性,只能JS引擎读取
2. 函数用来存储作用域链的容器,作用域链(AO、GO)
- AO:函数的执行期上下文- GO:全局的执行期上下文- 函数执行完成后,AO是被销毁的,- 如果再次执行这个函数的时候会重新生成一个全新的AO,- AO 是一个即时的存储容器
(2)作用域链
function a() {
function b() {
var b = 2;
}
var a = 1;
b();
}
var c = 3;
a();
页面打开的时候全局已经在执行,在执行的前一刻会生成GO -> 函数声明已经定义 全局执行的时候函数表达式
才赋值
1. 当函数 a 被定义的时候,
- 系统生成一个[[scope]]属性,[[scope]]中保存该函数的作用域链
- 在作用域链的第0位保存当前环境下的全局执行上下文GO
- GO 中保存全局下的所有对象,其中包含函数a 和全局变量c = 3
当函数 a 函数被执行时(前一刻,预编译的时候)
- 系统生成一个[[scope]]属性,保存该函数的作用域链
- 作用域链的顶端(第0位)保存a 函数生成的函数执行期上下文 AO
- GO 被保存到 作用域链的第1位
- 查找变量是从a 函数的作用域从顶端往下依次查找
a 在执行的同时函数 b 函数被定义时,
- 在a函数环境下,b 函数在定义的时候和a 函数被执行的时候作用域链是相同的
当函数 b 函数被执行时(前一刻,预编译的时候)
- 系统生成一个[[scope]]属性,保存该函数的作用域链
- 第 0 位保存自身的AO,a 函数的AO和全局的Go依次向下排列
当函数 b 函数执行结束的时候
- b 函数的AO 被销毁,回归b 被函数定义的时候的[[scope]]
当函数a 执行结束的时候
- 函数a 的 AO 被销毁 同时 b 函数的[[scope]]也销毁了
- a 函数 回归 a函数 被定义的时候
function a() { function b() { function c() { } c(); } b(); console.log(b) } a(); // 1. a 定义:a.[[scope]] -> 0 : GO // 2. a 执行:a.[[scope]] -> 0 : a.ao 1 : Go // 3. b 定义:b.[[scope]] -> 0 : a.ao 1 : Go // 4. b 执行:b.[[scope]] -> 0 : b.ao 1 : a.ao 2 : Go // 5. c 定义:c.[[scope]] -> 0 : b.ao 1 : a.ao 2 : Go // 6. c 执行:c.[[scope]] -> 0 : c.ao 1 : b.ao 2 : a.ao 3. Go // 7. c 销毁:c.[[scope]] -> 0 : b.ao 1 : a.ao 2 : Go // 7. b 销毁:b.[[scope]] -> 0 : a.ao 1 : Go // 7. a 销毁:a.[[scope]] -> 0 : GO(3)闭包基础
闭包就是能够读取其他函数内部变量的函数function test1() { var c = 3; function test2() { var b = 2; a++; console.log(a); } var a = 1; return test2; } var c = 3; var test3 = test1(); test3();
闭包
当内部函数被返回到外部并保存时,一定会产生闭包
闭包会产生原来的作用域链的不释放
过度的闭包可能会导致内存的泄露,或者加载太慢闭包demo
// 非闭包写法 var count = 0; window.onload = function(){ var button = document.getElementById("clickme"); button.onclick = handleClick; } function handleClick(){ var message = "you click me"; var div = document.getElementById("message"); count++; div.innerHTML = message + count + "times!"; } // 非闭包写法 count必须写在外面 不然每次都会进入函数重置 但是会引起变量重名的问题 // 闭包写法 window.onload = function (){ var count = 0; var message = "you clicked me"; var div = document.getElementById("message"); var button = document.getElementById("clickme"); button.onclick = function () { count++; div.innerHTML = message + count + "times!"; } }闭包实例
返回数组写法
function test(){ var n = 10; function add(){ n++; console.log(n); } function reduce(){ n--; console.log(n); } return [add,reduce]; //此处返回的是数组则下面的arr被赋值的就是数组 } var arr = test(); console.log(arr); //返回数组写法 arr[0](); arr[1]();返回对象写法
function test(){ var n = 10; function add(){ n++; console.log(n); } function reduce(){ n--; console.log(n); } return {add,reduce}; //此处返回的是对象则下面的arr被赋值的就是对象 } var arr = test(); console.log(arr); // 返回对象写法 arr.add(); arr.reduce();返回整个数组写法
function test2() { var sunSched = ''; var operationarr = [ function setSched(thing) { sunSched = thing; }, function showSChed() { console.log("my" + sunSched); } ] return operationarr; //直接返回整个数组写法 } var sunSched = test2(); console.log(sunSched); sunSched[0]('thing'); sunSched[1](); sunSched[0]('test1'); sunSched[1]();返回整个对象写法
function test() { var sunSched = ''; var operation = { setSched: function (thing) { sunSched = thing; }, showSChed: function () { console.log("my" + sunSched); } } return operation; //直接返回整个对象 } var sunSched = test(); sunSched.setSched('test1'); sunSched.showSChed();
