详细解读JavaScript变量、作用域链和垃圾回收机制

一、基本类型与引用类型的区别

1、基本类型和引用类型

JavaScript中有两种不同的数据类型:基本类型和引用类型。
基本数据类型有:Undefined、Null、Boolean、String和Number。存储在(stack)中的简单数据段,它们的值直接存储在变量访问的位置,即按值访问
引用类型有:Object、Function、Array存储在(heap)中的对象,存储在变量处的值是一个指针(point),指向存储对象的内存处,即按引用访问

2、动态属性

为引用类型创建动态属性:
var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"
为基本类型创建动态属性报错:
var name = "Nicholas";
name.age = 27;
alert(name.age); //undefined
这说明只能给引用类型值动态地添加属性,以便将来使用。

3、复制变量值

如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
var num1 = 5;
var num2 = num1;
在此, num1 中保存的值是 5。当使用 num1 的值来初始化 num2 时, num2 中也保存了值 5。但 num2中的 5 与 num1 中的 5 是完全独立的,该值只是 num1 中 5 的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量,如下面的例子所示:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"

4、检测类型

基本类型:使用typeof操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具,如果变量的值是一个对象或 null,则 typeof 操作符会像下面例子中所示的那样返回"object"。
var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();

alert(typeof s); //string
alert(typeof i); //number
alert(typeof b); //boolean
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
    引用类型:使用instanceof知道它是什么类型的对象,其语法如下所示:
result = variable instanceof constructor
举例:
alert(person instanceof Object); // 变量 person 是 Object 吗?
alert(colors instanceof Array); // 变量 colors 是 Array 吗?
alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?
    根据规定,所有引用类型的值都是 Object 的实例。因此,在检测一个引用数时, instanceof 操作符始终会返回 true。当然,如果使用 instanceof,则该操作符始终会返回 false,因为基本类型不是对象。

二、执行环境及作用域

首先明白以下几个重要概念之间的关系:
    
  • 执行环境:执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。
  • 变量对象:每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都包含在这个对象中。
  • 作用域链:当一段函数在执行环境中执行时,会形成一个变量对象的作用域链,当前环境的变量对象在最前端,其外部函数执行环境的变量对象在下一级,一直回溯到全局环境的变量对象。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
在 Web 浏览器中,全局执行环境被认为是 window 对象,因此所有全局变量和函数都是作为 window 对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁)。
下边再列举一个全局变量与局部变量的函数实例,方便理解:
    
这个例子中有三个执行环境:全局环境、 changeColor()的局部环境和 swapColors()的局部环境。全局环境中有一个变量 color 和一个函数 changeColor()。 changeColor()的局部环境中有一个名为 anotherColor 的变量和一个名为 swapColors()的函数,但它也可以访问全局环境中的变量 color。 swapColors()的局部环境中有一个变量 tempColor,该变量只能在这个环境中访问到。无论全局环境还是 changeColor()的局部环境都无权访问 tempColor。然而,在 swapColors()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父执行环境。看一下下边这个作用域链图相信会清晰很多:
    
总结:内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。

三、垃圾收集

JavaScript具有自动垃圾收集机制,也就是说,开发人员不用再像对待C和C++之类的语言一样手工跟踪内存的使用情况,现在所需内存的分配及无用内存的回收完全实现了自动管理。原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔周期性地执行这一操作。
垃圾收集器必须跟踪哪个变量有用哪个变量无用,对于不再有用的变量打上标记,以备将来收回其占用的内存,通常有两种策略用来标记无用变量:标记清除和引用计数

1、标记清除

标记清除是最常用的垃圾收集方式。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。而当变量离开环境时,则将其标记为“离开环境”
可以使用任何方式来标记变量。比如可以通过翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个“进入环境”变量列表及一个“离开环境”的变量列表来跟踪哪个变量发生了变化。说到底,如何标记变量其实并不重要,关键在于采取什么策略。
到 2008 年为止, IE、 Firefox、 Opera、 Chrome 和 Safari 的 JavaScript 实现使用的都是标记清除式的垃圾收集策略(或类似的策略),只不过垃圾收集的时间间隔互有不同。

2、引用计数

引用计数的含义是跟踪每个值被引用的次数当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋值给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间会收起来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
但是,引用计数遇到的一个很严重的问题就是循环引用。循环引用指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。请看下面这个例子:
function problem(){
var objectA = new Object();
var objectB = new Object();
objectA.someOtherObject = objectB;
objectB.anotherObject = objectA;
}
    在这个例子中, objectA 和 objectB 通过各自的属性相互引用;也就是说,这两个对象的引用次数都是 2。在采用标记清除策略的实现中,由于函数执行之后,这两个对象都离开了作用域,因此这种相互引用不是个问题。但在采用引用计数策略的实现中,当函数执行完毕后, objectA 和 objectB 还将继续存在,因为它们的引用次数永远不会是 0。假如这个函数被重复多次调用,就会导致大量内存得不到回收。

注:读《JavaScript高级程序设计》有感~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值