• 变量/作用域/内存问题知识点总结


    《JavaScript高级程序设计》Chapter4笔记

    1. 关于引用类型值的访问

    两种数据类型的值:基本类型值和引用类型值。

    引用类型的值是保存在内存中的对象,JavaScript不允许直接访问内存中的位置,即不能直接操作对象的内存空间。在操作对象时,实际上是操作对象的引用而非实际的对象。故引用类型的值是按引用访问的。

    2. 复制基本类型值和引用类型值的不同机制

    • 复制基本类型值:重新生成一个新值,将原值复制到位新变量分配的位置上。两个变量完全独立,新变量只是原值的一个副本,它们参与任何操作互相不影响。

    • 复制引用类型值:值的副本实际是一个指针,这个指针指向存储在堆中的一个对象。复制结束后,两个变量将引用同一个对象。改变其中一个变量,就会改变另一个。

        var obj1=new Object();
        var obj2=obj1;
        
        obj1.name="Nicholas";
        console.log(obj2.name);//"Nicholas"
      

    3. 函数参数的按值传递

    访问变量有按值和按引用两种方式,函数参数只能按值传递。

    • 传递基本类型值:被传递的值会复制给一个局部变量;

    • 传递引用类型值:该值在内存中的地址(指针)会复制给一个局部变量。

      以下例子说明即使引用类型的值是按值传递给参数的(传递的是地址),该参数也会按引用访问同一个对象。

        function setName(obj) {
            obj.name="Nicholas";
        }
        
        var person=new Object();
        setName(person);
        console.log(person.name);//"Nicholas"
      

      以下例子说明引用类型的值确实是按值传递的。因为如果person按引用传递,那么其就会自动被修改为指向name属性为“Greg”的新对象。但person.name不变,可见即使在函数内部修改了参数的值,原始的引用扔未改变。

      其实,函数内部重写obj时,这个变量引用的就是一个局部变量对象了,它会在函数执行完后自行销毁。

        function setName(obj) {
            obj.name="Nicholas";
            obj=new Object();
            obj.name="Greg";
        }
        
        var person=new Object();
        setName(person);
        console.log(person.name);//"Nicholas"
      

    4. typeof和instanceof检测类型比较

    • typeof: 检测变量是哪种基本数据类型

    • instanceof: 检测变量是不是某一种对象。引用类型的值检测Object都是true。基本类型的值检测Object为false,因为基本类型不是对象。

        //使用typeof
        var s="Nicholas";
        var b=true;
        var i=22;
        var u;
        var n=null;
        var o=new Object();
        var reg=new RegExp();
        
        console.log(typeof s);//string
        console.log(typeof b);//boolean
        console.log(typeof i);//number
        console.log(typeof u);//undefined
        console.log(typeof n);//object
        console.log(typeof o);//object
        console.log(typeof reg);//object,低版本Chrome和Safari返回function
       
        //使用instanceof
        var o=new Object();
      
        var arr=new Array();
        var reg=new RegExp();
        var i=22;
        
        console.log(o instanceof Object);//true
        
        console.log(arr instanceof Object);//true
        console.log(arr instanceof Array);//true
        
        console.log(reg instanceof Object);//true
        console.log(reg instanceof RegExp);//true
        
        console.log(i instanceof Object);//false
      

    5.有关执行环境和作用域的几个概念

    (1)变量对象
    • 每个执行环境都有一个对应的变量对象,该环境中定义的所有变量和函数都保存在这个对象中。
    • 全局执行环境对应window对象,故所有全局变量和函数都是window对象的属性和方法。
    • 某执行环境中的所有代码执行完后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。全局执行环境直到关闭网页或浏览器才会被销毁。
    (2)环境栈机制

    当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。函数执行完后,栈将其弹出,控制权又交还给之前的执行环境。

    (3)作用域链
    • 其用途是保证执行环境可以有序访问变量和函数。
    • 作用域链的前端是当前执行环境的变量对象。下一个变量对象来自包含环境。再下一个变量对象来自下一个包含环境,一直延续到全局执行环境的变量对象。
    • 内部环境可以通过作用域链访问所有的外部环境,外部环境不能访问内部环境的任何变量和函数。
    • 函数参数也相对于函数局部变量,访问规则同其他局部变量。

    eg1:

    var color1="blue";
    
    function changeColor1() {
        var color2="red";
        
        function changeColor2() {
            var color3=color2;
            color2=color1;
            color1=color3;
            
            console.log(color1+" "+color2+" "+color3);//red blue red
            //这里可访问color1、color2、color3
        }
        changeColor2();
        console.log(color1+" "+color2);//blue red
        //这里可访问color2、color3
    }
    
    console.log(color1);//blue
    //这里只能访问color1
    
    changeColor1();
    

    注意:一般不在函数声明内部再写函数声明。

    eg2:

    var color="blue";
    
    function getColor() {
        var color="red";
        return color;
    }
    
    console.log(getColor());//red
    

    6. JavaScript没有块级作用域

    注意和C++不同。

    7. 关于var声明

    • var声明的变量会被添加到最接近的环境中
    • 没有var则被添加到全局环境成为全局变量,不推荐不带var的声明。

    8.自动垃圾收集机制

    垃圾收集机制原理:找出不再继续使用的变量,然后释放其所占用的内存。垃圾收集器会按照固定的时间间隔周期性执行这一操作。

    局部变量只在函数执行的过程中存在,这个过程中会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。直至函数执行结束,局部变量就没有存在的必要了可以释放它们的内存。垃圾收集器必须跟踪哪个变量有用哪个变量没用。

    方式一:标记清除
    1. 垃圾收集器在运行的时候给存储在内存中的所有变量都加上标记;
    2. 然后,去掉环境中的变量及被环境中的变量引用的标记;
    3. 之后再被加上标记的变量就是准备删除的变量;
    4. 最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并收回它们所占的内存空间。
    方式二:引用计数

    引用计数即跟踪每个值被引用的次数。

    1. 声明了一个变量并将一个引用类型的值赋给该变量时,该值引用次数就是1;
    2. 若该值又被赋给了另外一个值,则该值引用次数加1,为2;
    3. 相反,如果包含对这个值引用的变量又取得了另一个值,则这个值引用次数减1;
    4. 当值的引用次数变为0时,说明没有办法再访问这个值了,故可将其所占的内存空间收回来;
    5. 当垃圾收集器再次运行时,它就会释放那些引用次数为0的值所占用的内存。

    方式二的问题:循环引用

    当两个对象互相引用的时候,它们的引用计数永远也不会为0,造成问题。

    浏览器采用方式

    目前主流浏览器都是标记清除式的垃圾回收策略。只不过时间间隔不同。

    IE有一部分对象不是原生JavaScript对象,其BOM和DOM中的对象就是C++编写的COM(组件对象模型),COM的垃圾回收机制是引用计数。故只要在IE中涉及COM对象就存在循环引用问题。 不过,IE9把BOM和DOM都换成了真正的JavaScript对象,故避免了该问题。

    9. 管理内存:提升性能的方式之一

    现实:分配给Web浏览器的可用内存数量通常比分配给桌面应用程序的少。这是出于安全方面的考虑,方式运行JavaScript网页耗尽全部系统内存而导致系统崩溃。

    故确保占用最少的内存可以让页面获得更好的性能。优化内存占用的最佳方式:为执行中的代码只保存必要的数据。

    优化方法:

    解除引用: 对全局变量在不需要使用它的时候将它手动设为null。

    不过,解除引用并不意味着自动回收该值所占的内存。解除引用的作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

  • 相关阅读:
    pandas之DataFrame
    python浅拷贝和深拷贝
    Numpy 机器学习三剑客之Numpy
    django--验证码功能实现
    python基础题
    python武器库
    django-rest-framework
    django--admin组件
    【转载】关于DBUtils中QueryRunner的一些解读
    【转载】java中的反射
  • 原文地址:https://www.cnblogs.com/Bonnie3449/p/5360980.html
Copyright © 2020-2023  润新知