day4(12.19):变量、作用域和内存问题

作者: 留白_汉服vs插画 | 来源:发表于2017-12-18 23:14 被阅读12次

    理解基本类型和引用类型的值

    理解执行环境

    理解垃圾收集

    基本类型和引用类型

    基本类型值:String、number、undefined、Boolean。对应四种值类型。

    引用类型值:可能有多个值构成的对象。在操作对象的时候很多大多情况操作它的引用,(除了添加属性)比如复制一个对象,其实就是一个复制一个对象的引用。引用类型的值是按引用访问的。所以称作引用类型。我这里跟书上不一样,把null放到引用类型了。因为nulltypeof的话,就是object。

    区别:

    1、只给引用类型值动态添加属性。基本类型是不可以的,添加了再访问也是undefined。

    2、复制的时候,基本类型因为占用空间少,所以直接新建一个值给另外一个变量。引用类型是对象,可能很多的属性,占用空间可能特别大,新建空间的话,浪费空间。obj1复制给obj2的话,直接让obj2页指向同一个对象的地址就好了。这样就会导致一个问题,就是obj1改变属性的时候,obj2指向相同的空间,也会发生改变。就像就是七伤拳,一伤皆伤。

    3、函数中传递参数的时候,如果是基本类型,就直接把给复制然后传递进去。但是如果是引用类型,传递的是引用类型的地址的值。所以会有这样问题,如果在函数体中修改参数的值,函数外基本类型是p事不影响,函数外的引用类型就会跟着一起被修改了,因为指向相同的地址。比如:

    function setName(count,obj){count = 20; obj.name = "嘿嘿嘿,还是王海洋";obj = new Object(),obj.name = "又是我"}

    调用setName(count1,obj1),外面的count1是不变的,但是obj1的name属性就改变了,变成了"嘿嘿嘿,还是王海洋",之后新声明了obj,从新申请了一个地址,不再指向obj1的地址了,所以不是变成"又是我"。

    4、检测类型 typeof 能正确检测number,Boolean,string,undefined。但是对object、array只能检测为object,function检测为function。针对引用类型使用instanceof。

    比如:person instanceof Object //person是Object类型吗?

    colors instanceof Array //colors是Array类型吗?

    pattern instanceof RegExp //pattern 是RegExp类型吗?

    执行环境以及作用域

    执行环境:定义了变量或函数有权访问的其他数据。变量对象:执行环境中所有的变量和函数都保存在这个对象里面。全局执行环境是最外的一个执行环境,为window对象。所有的全局对象和函数都作为window的属性和方法使用。执行环境被销毁,保存其中的变量和函数都销毁。作用域链:顶端是当前执行代码所在的环境的变量对象,作用域链的下一个是变量对象来自下一个包含环境,一直到全局执行环境。标识符解析的时候,就是沿着作用域链一级一级往上。

    window

                color

                changeColor()

                        anotherColor

                        swapColor()

                                tempColor

    只能沿着作用域链往上搜索,不能往下。对于swapColor()而言,作用域链中有3个对象:swapColor()的变量对象,changeColor()的变量对象,全局变量对象。同理changeColor()作用域链有两个对象:changeColor()的变量对象,全局变量对象。

    延长作用域链

    虽然只有函数作用域和全局作用域,但是有try-catch的catch语句和with语句来延长作用域链。with指定对象添加到作用域链中,catch会创建一个新的变量对象,包含的是被抛出的错误对象的声明。

    function bulid(){

    with(location){var url = href} } 

    with语句接收的是location对象,变量对象中就包含了location的属性和方法,也就是这个变量对象被添加到作用域链的前端了。

    没有块级作用域

    只有函数作用域和全局作用域,if,for中代码块中声明的变量,就如同在代码块之外声明一样。函数中如果声明时候没有var,就是全局中声明一样。

    查询标识符

    现在局部的环境中查,如果存在,则不用再查询父环境中的标识符。因此父环境中如果有同名的,会被忽略掉。

    垃圾收集

    开发人员不需要关心垃圾收集的问题,因为内存分配以及内存的回收完全实现了自动管理。回收的原理:垃圾收集器会按照固定的时间间隔找出不再继续使用的变量,然后释放占用内存。

    局部变量只有在函数执行过程中存在,执行完了之后,没有存在的必要了,就释放内存。

    标记清除

    1、存储在内存中所有的变量都加上标记,2、去掉环境中的变量以及环境中的变量引用的变量的标记。3、带标记的变量是无法访问的,销毁那些带标记得值,并回收它们所占的内存空间。

    引用计数

    跟踪记录每一个值的引用次数。当引用次数为0时,就可以将其内存收回来。有一个问题就是循环引用,比如obj1.a = obj2 ; obj2.b = obj1; obj1和obj2的引用次数都是2,无法清除的。导致大量的内存无法被回收。因此使用obj1.a = null ; obj2.b = null 来手工断开连接,消除循环引用。可以消除内存泄漏。(程序中动态分配的堆内存由于某种原因,程序未释放造成系统内存的浪费就是内存泄漏)

    内存管理

    一旦数据不在有用,最好通过将其值设置为null来释放引用,这个做法叫解除引用。使用与大多数的全局变量和全局对象属性。而局部变量会在离开执行环境自动解除引用。

    function createItem(){ item = new Object()}

    var newItem = createItem();

    这里的item是局部变量,会在离开执行环境自动解除引用。但是newItem 全局的,需要手工解除引用。newItem = null。接触引用是让值脱离执行环境,以便垃圾收集器下次运行将其回收。

    小结

    js两种类型值:基本类型5,引用类型

    基本类型:固定大小内存,保存在栈内存;复制的时候创建这个值的副本;用typeof;

    引用类型:不固定大小,保存在堆内存;复制的时候复制这个对象的指针;用instanceof;

    执行环境:

    只有全局和函数的执行环境之分;每次进入一个新执行环境,创建一个用于搜索变量和函数的作用域链;局部可访问父和全局,父和全局不能访问局部;

    垃圾回收:

    离开作用域的值自动标记为可回收,回收期间被删掉;标记清除是目前主要的,引用计数有循环引用的问题,不怎么用了;解除变量的引用不仅有助于消除循环引用,对垃圾收集也有好处;为了保证有效回收内存,及时解除对全局对象,全局对象属性以及循环引用变量的引用。

    相关文章

      网友评论

        本文标题:day4(12.19):变量、作用域和内存问题

        本文链接:https://www.haomeiwen.com/subject/papvwxtx.html