JavaScript高级程序设计——第4章:变量、作用域和内存问题

4.1.4 检测类型

虽然检测基本数据类型时typeof是得力助手,但在检测引用类型的值时我们并不想知道某个值是对象,而想知道它是什么类型的对象。为此ECMAScript提供了instanceof操作符:

result=variable instanceof constructor

ex:alert(person instanceof object)、alert(colors instanceof Array)、alert(pattern instanceof RegExp);

根据规定所有引用类型的值都是object实例。

typeof 正则 (不同浏览器,会返回function或者object)。ECMA-262规定任何在内部实现[[Call]]方法的对象都应该在应用typeof操作符时返回"function"。

4.2 执行环境及作用域

执行环境 (execution context)定义了变量和函数有权访问的其他数据,决定了它们各自的行为,每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中,虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台用到它。全局执行环境是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的,某个执行环境中的代码执行完毕的,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出时才会被销毁)。每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中,而在函数执行之后,栈将其环境弹出,将控制权返回给之前的环境。ECMAScript程序中的执行流正是由这个方便的机制控制着。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途是保证对执行环境有权访问的变量和函数的有序访问。作用域链的前端,始终是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始只包含一个变量,就是arguments对象(这个对象在全局环境是不存在的),作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象来自下一个包含环境,这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。标识符解析是沿着作用域链一级一级地搜索标识符的过程。

<script type="text/javascript"> var color = "blue";

function changeColor() {
    var anotherColor = "red";
    function swapColor()
    {
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        //这里可以访问tempColor、anotherColor、color
    }
    //这里可以访问color、anotherColor、但不能访问tempColor
    swapColor();
}

//这里只能访问color
changeColor();      
</script>
View Code

内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境的任何变量和函数。这些环境之间的联系是线性,有次序的。每个环境就可以沿作用域链向上搜索,但不能向下搜索。

4.2.1 延长作用域链

虽然执行环境的类型总工有两种——全局和局部(函数),但有其它办法延长作用域链。有些语句可以在作用域的前端临时增加一个变量对象,该变量对象会在代码执行后移除,在以下两种情况下会发生这种现象:

1)try-catch语句的catch块;

2)with语句; 

<script type="text/javascript"> 
function buildUrl() {
    var qs = "debug=true";
    with(location)
    {
        var url=href+qs;
    }
        return url;

}
</script>
延长作用域

with语句会将指定的对象添加到作用域链中。对catch语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。

4.2.2 没有块级作用域

<script type="text/javascript"> 
    if (true) {
        var color = "red";
    }
    alert(color);//red
    
    for(var i=0;i<10;i++){
    dosomething(i);
    }
    alert(i);//10
</script>
没有块级作用域

1)用var声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在with语句中,最接近的环境是函数环境;如果初始化变量时没有使用var,该变量会自动被添加到全局环境。

2)查询标识符(变量名等),局部环境存在着同名标识符,就不会使用父环境的标识符(window. 标识符名:全局变量);

4.3 垃圾收集

自动垃圾收集机制,执行环境负责。(需要了解不同浏览器的垃圾收集策略,有助于性能的优化。)

用于标识无用变量的策略可能会因实现而异,但具体到浏览器中的实现,则通常有两个策略:标记清除、引用次数

4.3.3  性能问题

垃圾收集器(垃圾收集例程)是周期性运行。

4.3.4  管理内存

分配给web浏览器的内存数量要比分配给桌面应用程序的内存数量要少,目的主要是出于安全的考虑,防止运行javascript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时会影响调用栈以及在同一个线程中能够同时执行的语句数量。因此,确保占用最少的内存可以让页面获得更好的性能,而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用——这个做法叫作解除引用(dereferencing)。这一做法适用于大多数的全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用,如下例所示:

 <script type="text/javascript">
        function createPerson(name)
        {
            var localPerson = new Object();
            localPerson.name= name;
            return localPerson;
        }
        var globalPerson = createPerson("Nicholas");
        //手工解除 globalPerson 的引用
        globalPerson=null ;
     </script>
管理内存

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

4.4  小结

JavaScript变量可以用来保存两种类型的值:基本类型值和引用类型值。基本类型值源自以下5种基本数据类型:Undefined、Null、Boolean、Number和String。它们具有以下特点:

1) 基本类型值在内存中占用固定大小空间,因此被保存在栈内存中;

2) 从一个变量向另一个变量复制基本类型的值,会创建这个值的复本;

3) 引用类型的值是对象,保存在堆内存中;

4) 包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针;

5) 从一个变量向另一人变量复制引用类型的值,复制的是指针,因此两个变量最终都指向同一个对象;

6)确定一个值是何种基本类型可以使用typeof操作符,确定一个值是何种引用类型可以使用intanceof操作符;

所有变量都存在于一个执行环境当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。以下的关于执行环境的几点总结:

1)执行环境有全局执行环境和局部(函数)执行环境之分;

2)每次进入一个新执行环境,都会创建一个用于搜索变量和函数的作用域链;

3)函数的局部环境不仅有权访问函数作用域中变量,而且有权访问其包含(父)环境,乃至全局环境;

3)全局环境只能访问在全局环境中定义的变量和函数,而不能直接访问局部环境中的任何数据;

4)变量的执行环境有助于确定何时释放内存;

JavaScript是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题。可以对JavaScript的垃圾收集例程作如下总结:

1)离开作用域的值将自动标记为可以回收,将在垃圾收集期间被删除。

2) “标记清除”是目前主流的垃圾收集算法,这种算法的思想是对当前不用的值加标记,然后再回收其内存;

3)另一种垃圾收集算法是“引用计数”,这种算法的思想是跟踪记录所有值被引用的次数,JavaScript引擎目前都不再使用这种算法;但IE在访问非原生JavaScript对象(如Dom对象),这种算法仍可能会导致问题。

4)解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效的回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。

原文地址:https://www.cnblogs.com/SmileX/p/5642996.html