使用具备垃圾收集机制语言编写程序,开发人员一般不必操心内存管理的问题。但是,JavaScript在进行内存管理即垃圾收集时面临的问题还是有点与众不同。其中最主要的一个问题,就是分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少。确保占有最少的内存可以让页面获得更好的性能,而优化内存占有最佳方式,就是为执行的代码值保存必要的数据。
基本类型值和引用类型值
ECMAScript变量包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值保存在栈内存中,而引用类型值保存在堆内存中。
5种基本类型Undefined、Null、Boolean、Number、String,它们的值在内存中分别占有固定大小的空间,因此可以把它们的值保存在栈内存中,也可以提高查询变量的速度,对于它们可以说是按值访问的。
如果是引用类型的值,则必须在堆内存中分配空间。因为它们的值大小不固定,因此不能保存在栈内存中,但内存地址的大小是固定的,所以可以将内存地址保存在栈内存中,再根据地址找到保存在堆中的值,这种查询方式叫做按引用访问。
动态属性
对于引用类型的值,可以为其添加属性和方法,也可以改变和删除其属性和方法,例如:
var myobj = new Object();
myobj.name = "obj";
alert(myobj.name);
但是不能给基本类型的值添加属性,例如:
var mystr = "string";
mystr.name = "str";
alert(mystr.name); //undefined
复制变量值
如果从一个变量向另一个变量复制基本类型的值,会在栈中创建一个新值,然后把该值复制到为新变量分配的位置上,例如:
var num1 = 5;
var num2 = num1;
如果从一个变量向另一个变量复制引用类型的值,同样会将存储在栈中的值复制一份放到为新变量分配的空间中。但是这个值的副本实际是指向存储在堆中的一个对象的指针,例如:
var myobj1 = new Object();
var myobj2 = myobj1;
myobj1.name = "obj";
alert(myobj2.name); //obj
传递参数
ECMAScript中所有函数参数都是按值传递的。在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量,在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量。
使用基本类型值传递参数比较简单,例如:
function add(num) {
num+= 10;
return num;
}
var number = 20;
var result = add(number);
alert(number); //20
alert(result); //30
如果使用引用类型值则稍复杂,例如:
function setName(obj) {
obj.name = "obj";
}
var myobj = new Object();
setName(myobj);
alert(myobj.name); //obj
但这并不表明局部作用域中修改的对象会在全局作用域中反映出来,对象仍然是按值(内存地址)传递的,例如:
function setName(obj) {
obj.name = "obj";
obj= new Object();
obj.name = "new obj";
}
var myobj = new Object();
setName(myobj);
alert(myobj.name); //obj
检测类型
typeof操作符可以检测一个变量是哪种基本类型,但在检测引用类型时,总是返回object。通常我们并不是想知道某个值是对象,而是想知道它是什么类型的对象,可以使用instanceof操作符,语法如下:
result = variable instanceof constructor
如果变量是给定引用类型的实例,那么instanceof操作符就会返回true。根据规定,所有引用类型的值都是Object的实例。
环境与作用域
执行环境定义了变量或函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。这个对象无法访问,但解析器会在后台使用它。全局执行环境是最外围的一个执行环境,在Web浏览器中,全局执行环境被认为是window对象。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。
当代码在一个环境中执行时,会创建由变量对象构成的一个作用域链,保证对执行环境有权访问的所有变量和函数的有序访问。全局执行环境是作用域链中的最后一环,标识符解析是沿着作用域链一级级搜索,从作用域链接前端(当前执行环境)开始,直到找到标识符为止。
作用域链的延长
有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除,即当执行流进入下列任何一个语句时,作用域链就会得到加长:
try-cache语句的catch块。
with语句。
块级作用域
JavaScript没有块级作用域,即花括号封闭的代码块中定义的变量可以在代码块外访问,例如:
if (true) {
varcolor = "blue";
}
alert(color);
变量声明
在使用var关键字声明变量时,这个变量将被自动添加到距离最近的可用环境中。如果变量在未经声明的情况下被初始化,那么该变量会被自动添加到全局环境。