二. 作用域链_内存管理

42 阅读4分钟

二. 作用域链_内存管理

补充点其他的: 1.箭头函数的this是一层一层网上找;

2.如果没有闭包,函数的上层作用域是Global;

3.如果有闭包,函数的上层作用域是引用变量的那个函数

2.1. 作用域链和面试题

如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链

函数的作用域和定义函数的位置有关系,与调用的位置没有关系;作用域链在预解析阶段就已经确认了

函数提升优先级高于变量提升

class内部自动开启有严格模式

 function foo() {
     var a = b=100
     // 等同 var a=100,b=100
 }
 foo()
 console.log(a);  //  a is not defined
 console.log(b); // 100
 // 执行到函数时,对函数预解析,将a提升到AO(不赋值),此时是预解析,所以解析return后面的变量
 var a = 100
 function foo() {
     console.log(a); // undefined
     return
     var a = 100
 }
 foo()
 var scope="global";
 function t(){
     console.log(scope);  // undefined
     var scope="local"
     console.log(scope);  // local
 }
 t();
 var message="hello"
 function foo(){
     console.log(message);    // hello
 }
 function demo(){
     var message="world"
     foo()
 }
 demo()

父级作用域我们常说是一层一层的网上找,这个说法不够准确,应该是闭包的情况下,才会逐层往上找,如果只是嵌套关系,则不会

  • 如果是嵌套函数(左),bar的父级作用域是GO,(foo的AO对象都被销毁了)

  • 如果是闭包(右),bar的父级作用域是foo的AO对象

    image-20220214230341702.png

    image-20220215130322381.png

2.2. 作用域链和闭包

最后fn()的结果是5,是因为他的父级作用域是之前创建的AO对象,第二个foo()的AO和之前的AO是互不干扰的,

 function foo() {
     let a = 1
     console.log("ffoo", a);
     function bar() {
         a++
         console.log("bar", a);
     }
     return bar
 }
 ​
 var fn = foo()
 fn()
 fn()
 fn()
 foo()
 fn()// 这一步打印值如果不理解 可以将上面的foo()改为 var fm = foo()  fm()

image-20220215103147487.png

 var x = 0
 function foo(x, y = function () { x = 3; console.log(x); }) {  
     console.log(x);// 注:这里是访问的参数的x的undefined
     var x = 2
     y()
     console.log(x);
 }
 foo()
 console.log(x);  
 // 当函数的参数 有默认值情况下,会多形成一个参数作用域,用于保存参数的值
 // 注:foo参数和函数体内的x不是同一个,分别有自己的作用域
 // undefined 3 2 0
 ​

image-20220403223504093.png

函数的参数有默认值会形成一个作用域,但是第五行打印时,函数内部会有预解析,会提升啊,那么第五行打印的应该是var a=undefined啊,他自己作用域有a 为什么还往上层找a?

2.3. 认识内存

  • 不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动的管理内存 某些编程语言会可以自动帮助我们管理内存

  • 不管以什么样的方式来管理内存, 内存的管理都会有如下的生命周期**:

    • 第一步:分配申请你需要的内存(申请);
    • 第二步:使用分配的内存(存放一些东西,比如对象等);
    • 第三步:不需要使用时,对其进行释放;
  • 不同的编程语言对于第一步和第三步会有不同的实现:

    • 手动管理内存:比如C、C++,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函数);
    • 自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存;
  • 我们可以知道JavaScript通常情况下是不需要手动来管理的

2.4. JS的内存管理

  • JavaScript会在定义变量时为我们分配内存。

  • 但是内存分配方式是一样的吗?

    • JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配;
  • JS对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用;

    image-20220210160607290.png

2.5. 垃圾回收

垃圾回收器我们也会简称为GC,所以在很多地方你看到GC其实指的是垃圾回收器

GC怎么知道哪些对象是不再使用的呢?这里就要用到GC的算法了

  • 引用计数

    • 当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;

    • 这个算法有一个很大的弊端就是会产生循环引用;

       var obj={ name:obj2};
       var onj2={ name:obj}
      

      image-20220210181726178-16547750467911.png

  • 标记清除

    • 这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象;
    • 这个算法可以很好的解决循环引用的问题;