[[scope]]
每个函数都是一个对象,其中有些属性是我们不可以访问,仅供js引擎存取的,[[scope]] 就是其中之一。[[scope]] 就是作用域,里面存储了执行期上下文对象的集合。这些集合呈链式链接,叫做作用域链。
看看下面这段代码:
function a(){
function b() {
var b = 234
}
var a = 123
b()
}
var glob = 100
a()
首先,a被定义的时候,a的scope属性指向的ScopeChain对象中的第0位会放GO
graph TD
a的ScopeChain --> 第0位
第0位 --> GlobalObject
然后a执行的前一刻,发生如下变化
graph TD
a的ScopeChain --> 第0位
a的ScopeChain --> 第1位
第0位 --> ActivationObject
第1位 --> GlobalObject
接下来b被定义时,b的scope第0位指向a的AO,第1位指向GO
graph TD
b的ScopeChain --> 第0位
b的ScopeChain --> 第1位
第0位 --> ActivationObject/a
第1位 --> GlobalObject
b被执行,再生成自己的AO,放到第0位
graph TD
b的ScopeChain --> 第0位
b的ScopeChain --> 第1位
b的ScopeChain --> 第2位
第0位 --> ActivationObject/b
第1位 --> ActivationObject/a
第2位 --> GlobalObject
b执行完,会断掉自己的AO链条,如果重新被执行,会再生成一个全新的AO,而不是原来的那一个。
我们在函数中查找变量时,会自顶向下查找作用域链。
闭包
当内部函数被保存到外部时,会生成闭包。缺点:闭包会导致原有作用域链不释放,造成内存泄漏。
function test(){
var arr = []
for(var i = 0; i < 10; i++){
arr[i] = function() {
console.log(i+'')
}
}
return arr;
}
var myArr = test()
for (var j = 0; j <10;j++){
myArr[j]()
}//打印10个10
函数体被保存到了外部,形成了闭包,10个函数体的AO都用的同一个。(这时 i 已经变成了10)
可以通过立即执行函数来形成独一无二的执行期上下文,来达到输出0到9的效果⬇️
function test(){
var arr = []
for(var i = 0; i < 10; i++){
(function (j) {
arr[j] = function() {
console.log(j+'')
}
}(i))//每次执行后被销毁,然后再创建一个全新的立即执行函数,因此每个立即执行函数都有自己独立的AO
}
return arr;
}
var myArr = test()
for (var j = 0; j <10;j++){
myArr[j]()
}//0,1,2,3,4,5,6,7,8,9
立即执行函数
针对初始化功能的函数。执行完就会销毁。
var num = (function cal(a,b){
return (a+b)
}(1,2))
cal //执行完就找不到这个函数了,会报错。函数名可写可不写。
num //3 可以有返回值。
有两种写法:
(function (){}()) //W3C建议这种写法。会先识别最外层的括号,把里面的变成表达式,再用括号执行。
(function (){}) ()
只有表达式才能被执行,被括号括起来就是表达式了(或者赋值给变量、或者前面加个'+'、'!'等等),后面加个()(执行符号)就被执行了。