变量、作用域和内存问题

时间:2023-02-04 06:11:02

基本类型和引用类型的值

基本类型值

指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。

基本数据类型:Undefined、Null、Boolean、Number、String。这5种基本数据类型的值在内存中分别占有固定大小的空间,因此可以把他们的值保存在栈内存中,这样提高查询变量的速度。我们操作的是他们实际保存的值,所以他们是按值访问的。

引用类型值

指的是那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。

查询引用类型的变量时,先从栈中读取内存地址,再找到保存在堆中的值。这种查询变量值的方式,叫按引用访问。

基本类型值和引用类型的总结

  1. 可以修改引用类型值的属性和方法,不能给基本类型添加属性和方法。
  2. 从一个变量向另一个变量复制基本类型的值,会在栈中创建一个新值,然后把该值复制到为新变量分配的位置上。两个变量不会相互影响
  3. 从一个变量向另一个变量复制引用类型的值,同样会将存储在栈中的值复制一份到为新变量分配的空间中,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。两个变量实际上将引用同一个对象,改变其中一个就会影响另一个。
  4. 传递参数
    function setName(obj){
    obj.name="Nicholas";//改变对象的name属性
    obj = new Object();//重新定义obj,obj中的值改变,obj指向另一个新对象。
    obj.name="Greg";//这个对象属性的改变和person所指向的对象无关。
    }
    var person = new Object();
    setName(person);//person的值复制一份给obj,obj和person指向同一个对象
    alert(person.name); //Nicholas

  5. 检测类型确定一个值是哪种基本类型可使用typeof,确定一个值是哪种引用类型可以使用instanceof

 

 

执行环境及作用域

变量对象:每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。
当代码在一个环境中执行时,会创建由变量对象构成的一个作用域链。作用域链的用途是保证对执行环境有访问权的所有变量和函数的有序访问。(由局部一层一层到全局)

延长作用域链

try-catch语句的catch块
其变量对象中包含的是被抛出的错误对象的声明
在IE中,catch语句捕获的错误对象会被添加到执行环境的变量对象中,即使是在catch块的外部也能访问到错误对象。
with语句
其变量对象包含着为指定对象的所有属性和方法所做的变量声明。
在with和catch中声明的变量都会被添加到执行环境的变量对象中
function buildUrl(){
var qs="?debug=true";
with(location){
var url=href+qs;
}
return url;//url被添加到执行环境的变量对象中,所以可以在执行环境中访问url
}

var result=buildUrl();
alert(result);



2)没有块级作用域
在其他类C的语言中,由花括号封闭的代码块都有自己的作用域。
if语句、for语句中创建的变量都会被添加到当前执行环境中。

for(var i=0; i<10; i++){
doSomething(i);
}
alert(i);//10
function add(num1,num2){
sum = num1+num2;//变量初始化时不使用var,该变量自动添加到全局环境
return sum;
}
var result = add(10,20);//30
alert(sum);//30



垃圾收集

JS具有自动垃圾收集机制,也就是说执行环境会负责管理代码执行过程中使用的内存。
垃圾收集器会按照固定的时间间隔,周期性的释放那些不再继续使用的变量占用的内存。

标记清除

垃圾收集器在运行的时候会给存储在内存中的每个变量都加上标记,然后去掉环境中的变量和被环境中变量引用的变量的标记,销毁那些仍带有标记的值并回收他们所占用的内存空间。

引用计数

跟踪记录每个值被引用的次数。
垃圾收集器运行时会释放那些引用次数为0的值所占用的内存。

循环引用

function problem(){
var objectA=new Object();
var objectB=new Object();

objectA.someOtherObject = objectB;
objectB.someOtherObject = objectA;
}



objectA和 objectB通过各自的属性相互引用,这两个对象的引用次数都是2。当函数执行完时,objectA objectB还继续存在,其占用的内存不能被收回
IE中有一部分对象并不是原生JS对象,其BOM和DOM对象就是使用C++以COM对象的形式实现的,而COM对象采用的垃圾收集机制就是引用计数,在IE中只要涉及COM对象,就会存在循环引用的问题
为了避免类似这样的循环引用问题,最好是在不使用他们的时候手工断开原生Js对象与DOM元素之间的连接。
myObject.element=null;将变量设置为null意味着切断变量与它此前引用的值之间的连接。垃圾收集器下次运行时,就会删除这些值并回收它们所占用的内存。
有的浏览器可以触发垃圾收集过程,但不建议这样做,在IE中调用window.CollectGarbage()会立即执行垃圾收集,在Opera 7+调用window.opera.collect()会启动垃圾收集例程。

管理内存

分配给浏览器的内存数量要比分配给桌面应用程序的少,目的是防止运行JS的网页耗尽全部系统内存导致系统崩溃。内存限制问题不仅影响给变量分配内存,还影响调用栈以及在一个线程中能同时执行的语句数量。
优化内存占用的最佳方式是为执行中的代码只保存必要的数据,一旦数据不再有用最好通过将其值设置为null来释放其引用(解除引用,这一方法适用于大多数全局变量和全局对象的属性)。