一 预编译
1概念
1)什么是预编译:
2)全局对象(Global)GO window
全局变量
全局函数
3)活动对象AO local
局部变量
局部函数
2 全局预编译
1) 流程:1找变量声明作为Go对象的属性名,值为undefined。2找函数声明,作为Go对象的属性值,值为function。
var a;变量声明 function a(){}; 函数声明;
流程:
(1)先把所有scirpt标签过一遍,然后整合在一起。
(2)全局预编译
1 产生window对象。GO
2 查找变量的声明,把a最为window对象的属性名,属性值为undefined。
3 查找函数的声明,把函数名a作为window对象的属性名,属性值是function
(3)全局预编译结束后,从上到下,依次执行代码。
3函数预编译:
1)流程:
1函数被调用的时候,为当前函数产生AO对象。
2 查找形参和变量作为AO对象的属性名,作为undefined
3 使用实参值改变形参的值。
4 查找函数声明,作为AO对象的方法,值为function。
test是AO对象的形参,i是AO对象的属性,b是AO对象的方法。
// 当调用函数a的时候,生产函数a的AO对象。进入函数预编译。
//AO:1 查找形参test,变量i作为i的AO对象属性名,值为undefined。
// 2形参‘1’赋值赋值给test。test:1
// 3查找局部函数 b,b作为AO对象的属性名,值为function。
function a(b,c){ console.log(b) var b=0; consoel.log(b) function b(){ console.lgo(22) } console.log(c); } a(1)
a:AO{
b:fun,
c:undefinde.
}
function a(i){ var i;//在预编译阶段就执行了,正式阶段不会执行。 console.log(i)//i=1; }
a(1);
一般来说优先级:
局部函数>实参>形参。
总结:1找声明;2 找赋值和调用。3 调用函数 进行AO对象,进行,函数作用域分析。
4找声明 形参 和变量为undefined 5赋值形参。找函数什么。6 找赋值和找调用。
二 作用域 和作用域链
1概念
1)域
全局作用域:有<script>标签产生的区域,从计算机的角度可以理解为window对象。
局部作用与:由函数产生的区域。从计算机的角度理解:函数的AO对象。
ES6之前,没有块作用域。
2)作用域链
函数存在一个隐士属性,[scopes],也称为作用于链,我们可以把它理解称为一个数组。
分析1)当在函数a外部的时候,作用域只有一个。就是Global
当在函数a内部的时候, 作用域下有两个,一个是local,一个Global。
当然我们看,当只有一个函数的时候,它的作用域是什么。(console.dir(a)打印a的内部属性)
分析2)
function a(){ console.dir(a); function b(){ console.dir(b); function c(){ console.dir(c); } c(); } b(); } a();
0产生全局作用域
1产生a函数的AO对象,aAO
函数a的scopes:
0:aAO
1:GO
2 产生b函数的AO对象,bAO
3产生c函数的AO对象。
2作用
作用域链的查找规则:
function a(){ var aa=111 function b(){ var aa=222 console.log(aa)} b()
} a();
1 产生a的AO对象,aAO
aAO:{ aa:111,b:fun}
2 产生b的AO对象,bAO
bAO:{aa:22}
在内部可以使用外部的函数,但是外部使用不了内部。因为b的AO对象没有产生。
三 闭包
视频:https://www.bilibili.com/video/BV1C54y1r7VS?p=13
博客:https://www.cnblogs.com/sandaizi/p/11582488.html
1外包的形成:
内部函数使用外部函数的变量,就是形成闭包。闭包是当前作用域的延伸。
案例1
function a(){//外部函数 var aa=111 function b(){ //内部函数 console.log(aa)}//aa是外部函数的变量,立刻,产生闭包。但是闭包并没有保存下来 b() } a();
案例2
function a(){ var aa=100 function b(){ console.log(b);}//形成闭包,{b:fun},但是结束被销毁了指针引用销毁了, b() } a();
案例2
function a(){ var aa=100 function b(){ var aa=10; console.log(aa);}// 不会形成闭包。因为,没有引用外部函数的变量 b() } a();
2 闭包的保持:(真正形成闭包,且不会消失!)
案例三:
function outerFunction() { var num = 0; function inner() { console.log(num++); } return inner;//inner就是一个闭包函数,因为可以访问到outerFcuntion的作用域。 } var demo = outerFunction(); console.log(demo); demo(); demo();
闭包产生的条件:
//闭包要形成:1内部函数使用外部函数的变量。
//闭包要保持:2 把内部函数返回到外部函数的外面。这样闭包才不会消失!!!
四 闭包的应用:
1闭包的两面性:
好处:一般来说,函数外部是不能访问函数内部的变量。也就是设计闭包。
坏处: 不注意使用闭包,出现奇怪的效果。
(1)《JavaScript高级编程》由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多。并不会垃圾回收。而且因为作用域链的原因。所有内存占用多。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
2 闭包的作用:
1 函数外部访问私有变量:
2 实现封装。
3 防止污染全局函数。
function Person() { var uname function setName(uname) { this.name = uname } function getName() { return this.uname } return { getName: getName, setName: setName, } } var xiao = new Person() console.dir(xiao) xiao.setName('xiaoli') console.log(xiao.getName)