概览

一、作用域


[[scope]]
函数创建时,生成的一个js内部的隐式属性。

二、作用域链


scope chain
决定了各级上下文中的代码在访问变量和函数时的顺序。

三、预编译


AO - activation object 函数的执行期上下文。
GO - global object 全局的执行期上下文。
函数执行前会生成AO。
函数执行完成后,自身的AO会销毁。

作用域

函数也是对象,是引用类型
对象就有属性

有隐式属性、显示属性
显示属性可以访问,隐身属性不能访问,隐式属性是js引擎内部固有的,就像对象的私有属性,虽然不能.得到,但是存在。

[[scope]]—-中文释义范围

定义:1函数创建时生成时,创建的一个Js引擎内部固有的隐式属性,只能由Js引擎读取
2.函数存储作用域链的容器

作用域链AO/GO
AO函数执行期上下文
GO全局的执行期上下文

函数执行完成后,AO是要被销毁的,AO是一个即时的存储容器
image.png

示例详解

image.png

image.png

a函数声明,不运行时,就有[[scope]]属性,存着GO
AO只有当函数执行时才有,函数如果不执行就不会有,在函数执行前一刻生成,是在执行前产生的,不是在执行时产生的,AO产生完函数没有执行
因为不运行没有AO,所以不存a函数的AO

image.png

a函数执行前一刻AO产生,AO把GO挤下去,成为第1个

image.png

b函数定义时,没有运行,所以没有形成b的AO,所以b的[[scope]]中只有GO与a的AO,定义时的[[scope]]永远和上一级被执行时一样

就像继承就写了个extends一样,继承父亲的所有,但只增不减

image.png

b函数执行时AO产生,产生的AO进入b函数的[[scope]]中

image.png

当b函数执行完,b函数的AO被销毁,回归定义的状态,最上面的被销毁

a函数不变,因为b函数执行完,之后a函数才结算执行完

b.scope中有b的AO,a的AO,GO的地址引用,这些引用被销毁,因为以后用不到b的AO了,为什么用不到,因为b的AO的引用被删除了,没有任何关于b的AO的引用了,所以b的AO就被删除了。a的AO,go依然有引用指向它们,所以不删除。

当a函数执行完,a函数的AO销毁,a.AO中有function b的声明,其中有b的scope,b的scope就被删除,函数b就不存在了。函数b就彻底没有了,就相当于原始代码根本没有写function b{…},没有函数b的声明,虽然原始代码有

当x的上一级结束被销毁,x的scope才被销毁

机器只看,只执行经过预编译之后的代码,原始的代码不看,不执行,预编译完再执行原始的代码就白预编译了,经过预编译之后的代码有类似的地方,也有不同,不同之处:引用值空间的地址等

依此类推,最后回到GO

问题

AO、GO是怎么存在scope里面的?

引用值,存的是地址

为什么是引用值?

大数据量占内存,占资源,如果想共用就指向大资源

GO、AO什么时候产生?

GO、AO都是预编译,预编译是在执行前。

GO在代码运行时,全局上下文,整段代码要运行,先产生GO,不运行不产生GO

AO是函数上下文,当函数要运行时产生AO,不运行则没有

为什么要有作用域链

为了判断变量之间关系,函数也是对象,是变量

代码题目

  1. /**
  2. * 1.
  3. * GO = {
  4. * a : function a() {}
  5. * }
  6. * 2. a函数执行前生成自身的AO
  7. * AO = {
  8. * b : function b() {}
  9. * }
  10. * 3. b函数执行前生成自身的AO
  11. * AO = {
  12. * c : function c() {}
  13. * }
  14. * 4. c函数执行前生成自身的AO
  15. * AO = {}
  16. */
  17. function a() {
  18. function b() {
  19. function c() {
  20. }
  21. c();
  22. }
  23. b();
  24. }
  25. a();

答案

1.GO = {
a : function a() {}
}
2. a函数执行前生成自身的AO
AO = {
b : function b() {}
}
3. b函数执行前生成自身的AO
AO = {
c : function c() {}
}
4. c函数执行前生成自身的AO
AO = {}

a定义:a.[[scope]] -> 0: GO
a执行:a.[[scope]] -> 0: a的AO
1:GO
b定义:b.[[scope]] -> 0:a的AO
1:GO
b执行:b.[[scope]] -> 0: b的AO
1:a的AO
2:GO
c定义:c.[[scope]] -> 0: b的AO
1:a的AO
2:GO
c执行:c.[[scope]] -> 0:c的AO
1:b的AO
2:a的AO
3:GO
c结束:c.[[scope]] -> 0:b的AO
1:a的AO
2:GO
b结束:b.[[scope]] -> 0:a的AO
1:GO
c.[[scope]]销毁
a结束:a.[[scope]] -> 0: GO
b.[[scope]] -> 销毁

答案简化

GO={a:func a}
a.AO={b:func b}
b.AO={c:func c}
c.AO={}

a定义 a.scope->0:GO
a执行 a.scope->0: a.AO
1:GO
b定义 a.scope->0: a.AO
1:GO
b执行 a.scope->0: b.AO
1: a.AO
2:GO
c定义 c.scope->0: b.AO
1: a.AO
2:GO
c执行 c.scope->0: c.AO
1: b.AO
2: a.AO
3: GO
c结束 c.scope->0: b.AO
1:a.AO
2:GO
b结束 b.scope->0 a.AO
1: GO
c.scope 销毁
a结束 a.scope->0:GO
b.scope销毁

闭包

从1:30开始看

代码示例

  1. function test1() {
  2. function test2() {
  3. var b = 2;
  4. console.log(a); // 1
  5. }
  6. var a = 1;
  7. return test2;
  8. }
  9. var c = 3;
  10. var test3 = test1();
  11. test3()

答案:1

GO={test1:func test1,c:u->3,test3:u->func test2}
test1.AO={
test2:func,a:u->1
}
test2.AO={
b:u->2
}

问题

这个代码undo时发生了什么 do时发生了什么

undo
GO={test1:func test1,c:u,test3:u}
test1.AO={test2.func,a:u}
test2.AO={b:u}
do
GO={test1:func test1,c:u->3,test3:u->func test2}
test1.AO={test2.func,a:u->1}
test2.AO={b:u->2}

test1定义: test1.scope->0:GO
test1执行: test1.scope->0:test1.AO
1:GO
test2定义: test2.scope->0:test1.AO
1:GO
test2执行: test2.scope->0:test2.AO
1:test1.AO
2:GO
test1结束: test1. scope->0:GO

test1先结束,执行return test2,test1中AO的引用地址消失,但test2在。 test2函数此时没有执行,var test3 = test1()其实test3就是test2,test2执行

test2结束: test2.scope->0:test1的AO
1:GO
test1.scope销毁

应该是
test1 定义 :test1.scope->0:GO
test1 执行:test1.scope->0:test1.AO
1:GO
test2定义:test2.scope->0:test1.AO
1:GO
test1结束:test1.scope->0:GO
test1函数先结束,return了,函数肯定执行完成,test1.AO的地址删除,只有GO的地址

test3(test2) 执行:test2.scope->0:test2.AO
1:test1.AO
2:GO
test3结束:test2.scope->0:test1.AO
1:GO
因为test2被go的test3引用,解收,因为go不会被销毁,test3不会被销毁,所以test2永远不会被销毁,
test2中的test2.scope->0:test1.AO 1:GO不会被销毁,永远存在,test2.ao运行结束后被销毁
等再运行时,继续生成test2.ao,运行多次,重复生成ao,销毁ao

test1定义 test1.scope 0:GO
test1执行 test1.scope 0:test1.AO
1:GO
test2定义 test2.scope 0:test1.AO
1:GO
test1结束 test1.scope 0:GO
test3(test2) 执行 test2.scope 0:test2.AO
1:test1.AO
2:GO
test2结束 test2.scope 0:test1.AO
1:GO

test1 dec :test1.scope->0:GO
test1 do:test1.scope->0:test1.AO
1:GO
test2 dec:test2.scope->0:test1.AO
1:GO
test1 done:test1.scope->0:GO

test3(test2) do:test2.scope->0:test2.AO
1:test1.AO
2:GO
test3 done:test2.scope->0:test1.AO
1:GO
image.png
GO储存test1:func,c:u,test3:u

image.png
AO执行,test2声明,test2.scope生成

image.png

return返回test2,test2函数没有执行,没有test2的AO,虽然test1执行完成,但test2被test3引用,所以test2不销毁,test2.scope没被销毁,test2.scope依然有test1的AO和GO的引用(的地址),所以存储他们的空间不被销毁,相当于代码中的函数test1依然存在
这时test1函数执行完成,因为都执行return了,函数肯定运行完毕了
test1.scope的test1.AO地址销毁,只有GO

image.png

test3执行,是test3指向的空间内的test2执行,test2的AO生成,test2.scope加入test2的AO,test2.AO没有变量a,往test1.AO找,找到变量a=1,所以打印1

image.png

当test3执行后,test2.AO被销毁,但test1.AO,GO并不会销毁,因为他们存在变量test3中,test3是全局变量永远存在,test1.AO与GO一样永远不会被销毁,GO不会被销毁

基础总结

当内部函数被返回到外部并被保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏,或加载过慢。
因为内部函数保存着外部函数的AO,内部函数被全局变量指向,外部函数的AO地址存在全局变量中,所以不会被释放
一般只有GO不销毁,常驻内存,但闭包会使AO运行完之后不会被销毁,常驻内存

闭包可以看作是一个现象,写闭包就是写一个能产生闭包现象的函数

作业

  1. function test() {
  2. var n = 100;
  3. function add() {
  4. n++;
  5. console.log(n);
  6. }
  7. function reduce() {
  8. n--;
  9. console.log(n);
  10. }
  11. return [add, reduce];
  12. }
  13. var arr = test();
  14. arr[0]();//101
  15. arr[1]();//100

因为test.AO没有被销毁,常驻内存,n没有被销毁
返回多个方法,用数组

GO test func

arr u->[add,reduce]
test.AO n u->100

add func

reduce func
GO test:func
arr:u->[add,reduce]
test.AO n:u->100
add:func
reduce:func
test声明 test.scope 0:GO
test执行 test.scope 0:test.AO
1:GO
add声明 add.scope 0:test.AO
1:GO
reduce声明 reduce.scope 0:test.AO
1:GO
test结束 test.scope 0:GO
add执行 add.scope 0:add.AO
1:test.AO
2:GO
add结束 add.scope 0:test.AO
1:GO
reduce执行 reduce.scope 0:reduce.AO
1:test.AO
2:GO
reduce结束 reduce.scope 0:test.AO
1:GO
  1. function sunSched() {
  2. var sunSched = '';
  3. var operation = {
  4. setSched: function(thing) {
  5. sunSched = thing;
  6. },
  7. showSched: function() {
  8. console.log('My schedule on sunday is ' + sunSched);
  9. }
  10. }
  11. return operation;
  12. }
  13. var sunSched = sunSched();
  14. sunSched.setSched('studying');
  15. sunSched.showSched();

返回多个方法用对象

GO sunSched:u->func->operation{}
sunSched.AO sunSched:u->’’->’studying’
operation:u->{…}
setSched.AO thing:’studying’
showSched.AO
  1. /**
  2. * GO = {
  3. * c : undefined -> 3
  4. * test3 : undefined -> function test2() {}
  5. * test1 : function test1() {}
  6. * }
  7. *
  8. * test1的AO
  9. * AO = {
  10. * a : undefined -> 1
  11. * test2: function test2() {}
  12. * }
  13. *
  14. * test2的AO
  15. * AO = {
  16. * b : undefined -> 2
  17. * }
  18. */
  19. function test1() {
  20. function test2() {
  21. var b = 2;
  22. console.log(a); // 1
  23. }
  24. var a = 1;
  25. return test2;
  26. }
  27. var c = 3;
  28. var test3 = test1();
  29. test3();
  30. /**
  31. * test1定义: test1.[[scope]] -> 0: GO
  32. * test1执行: test1.[[scope]] -> 0: test1的AO
  33. * 1:GO
  34. * test2定义: test2.[[scope]] -> 0: test1的AO
  35. * 1:GO
  36. * test2执行: test2.[[scope]] -> 0: test2的AO
  37. * 1: test1的AO
  38. * 2:GO
  39. * test1结束: test1.[[scope]] -> 0: GO
  40. * test2结束: test2.[[scope]] -> 0: test1的AO
  41. * 1: GO
  42. * test1.[[scope]] 销毁
  43. *
  44. */
  45. //--------------------------------------------------------------------------
  46. /**
  47. * GO = {
  48. * arr : undefined -> [add, reduce]
  49. * test: function test() {}
  50. * }
  51. *
  52. * AO = {
  53. * n : undefined -> 100
  54. * add : function add() {}
  55. * reduce: function reduce() {}
  56. * }
  57. *
  58. */
  59. function test() {
  60. var n = 100;
  61. function add() {
  62. n++;
  63. console.log(n);
  64. }
  65. function reduce() {
  66. n--;
  67. console.log(n);
  68. }
  69. return [add, reduce];
  70. }
  71. var arr = test();
  72. arr[0]();
  73. arr[1]();
  74. /**
  75. * test定义: test.[[scope]] -> 0 : GO
  76. * test执行: test.[[scope]] -> 0 : test的AO
  77. * 1 : GO
  78. * add定义: add.[[scope]] -> 0: test的AO
  79. * 1: GO
  80. * add执行: add.[[scope]] -> 0: add的AO
  81. * 1: test的AO
  82. * 2: GO
  83. * reduce定义: reduce.[[scope]] -> 0: test的AO
  84. * 1: GO
  85. * reduce执行: reduce.[[scope]] -> 0: reduce的AO
  86. * 1: test的AO
  87. * 2: GO
  88. */
  89. //--------------------------------------------------------------------------
  90. /**
  91. * GO = {
  92. * sunSched : undefined -> function sunSched() {} -> {...}
  93. * }
  94. *
  95. * AO = {
  96. * sunSched : undefined -> ''
  97. * operation: undefined -> {...}
  98. * }
  99. *
  100. */
  101. function sunSched() {
  102. var sunSched = '';
  103. var operation = {
  104. setSched: function(thing) {
  105. sunSched = thing;
  106. },
  107. showSched: function() {
  108. console.log('My schedule on sunday is ' + sunSched);
  109. }
  110. }
  111. return operation;
  112. }
  113. var sunSched = sunSched();
  114. sunSched.setSched('studying');
  115. sunSched.showSched();
  116. /**
  117. * sunSched定义:sunSched.[[scope]] -> 0: GO
  118. * sunSched执行:sunSched.[[scope]] -> 0: sunSched的AO
  119. * 1: Go
  120. * setSched定义: setSched.[[scope]] -> 0: sunSched的AO
  121. * 1: Go
  122. * showSched定义: showSched.[[scope]] -> 0: sunSched的AO
  123. * 1: Go
  124. */

image.png