之前说到的 function创立一个独立的作用域 其实不太准确
1.隐式属性
每个对象都有它的属性跟方法。
方法就是键名后面跟匿名函数
属性就是键名后面加字符串等
函数function就是一个对象,它也有它的属性
函数也是一种对象类型、引用类型、引用值
对象有些属性是我们无法访问的,这些属性就是JS引擎内部固有的隐式属性
(这些隐式属性是不供外部访问的,也可以理解为内部的私有属性)
我们研究这些隐式属性就是为了更好的理解其原理,更好的写代码
如下面这个 隐式属性:[[scope]] (scope就是域的意思):
- 1.是函数创建时,生成的一个JS内部的隐式属性,只能 JS 引擎读取
- 2.它是函数存储作用域链的容器。
作用域链里面存储的就是AO(函数的执行期作用域)、GO(全局的执行期作用域)
细节:当函数执行完成以后,AO是要销毁的,下一次执行要重新建立一个新的AO, 也就是说AO是即时的存储容器
2. [[scope]]步骤
- 步骤1
- 生成GO(所以说每一个函数作用域链里面都有一个GO)
- 当函数被定义时 生成了scope
- 步骤2
- a()执行时生成AO;
最顶端生成AO;GO往下移
一个代码块中。每一个函数都有一个自己的AO,和共同(全局)的GO!
- 步骤3
- 步骤4
- 步骤5
例子:
function a(){
function b(){
function c(){
} c(n);
} b(n);
} a();
a 定义: a.[[scope]] ->第0位 0 : GO
a 执行: a.[[scope]] -> 第0位 0: a -> AO; 1 : GO
b定义: b.[[scope]] -> 第0位 0 : a ->AO; 1 : GO
b 执行: b.[[scope]] ->第0位 0 : b ->AO; 1 : a ->AO 2: GO
c 定义: c.[[scope]] -> 第0位 0 : b ->AO; 1 : a-->AO; 2:GO
c 执行: c.[[scope]] ->第0位 0 : C-->AO 1:b ->AO; 2 : a-->AO; 3:GO
c 结束: c.[[scope]] 1:b ->AO; 2 : a-->AO; 3:GO
b 结束: b.[[scope]] -> 第0位 0 : a ->AO; 1 : GO 同时c.[[scope]] 没有了
a 结束: a.[[scope]] ->第0位 0 : GO 同时b.[[scope]] 没有了
3.闭包
<script>
function test1(){
function test2(){
var b = 2;
consolie.log(a);
}
var a = 1;
return test2();
}
var c = 3;
var test3 = test1();
test3();
<script>
步骤1:
步骤2:
步骤3: test2此时没有执行(默认最后执行里面的函数(b执行前一刻c定义,之后c再执行),即在test1执行时:执行完var a=1、return test2后再执行test2()函数,但是return这个东西,一旦执行就直接结束整个方法 所以此时test 2依旧不执行)
步骤4:
步骤5:
总结:
* 当内部函数被返回到外部并保存时,一定会产生闭包(一般出现return会出现闭包)
- 闭包会产生原来的作用域链不释放, 所以过度的闭包可能会导致内存泄漏(别人的AO太多了,占用内存),或加载过慢。
闭包实例中的作用:
<script>
/面包管理
function breadMgr(num){
var breadNum = arguments[0] || 10;
function supply(){
breadNum += 10;
console.log(breadNum);
}
function sale() {
breadNum --;
console.log(breadNum);
}
return [supply,sale];
}
var breadMgr = breadMgr(50);
breadMgr[0]();//60
breadMgr[1]();//59
breadMgr[1]();//58
breadMgr[1]();//57
breadMgr[0]();//67
breadMgr[0]();//77
breadMgr[1]();//76
</script>
上述共用一个breadMgr(num)的AO,所以递归过来的两个方法一起改变这个AO;