红皮书(3):变量、作用域和内存题目

基础范例和援用范例的值

动态的属性

var person = new Object();
person.name = "Nicholas";
alert(person.name); // Nicholas
var name = "Nicholas";
name.age = 27;
alert(name.age); // undefined

这申明只能给援用范例值动态增加属性。

复制变量值

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

变量obj1保留了一个对象的新实例。然后,这个值被复制到了obj2中;换句话说,obj1和obj2都指向同一个对象。如许,当为obj1增加name属性后,可以经由过程obj2来接见这个属性。

通报参数

function addTen(num){
    num +=10;
    return num;
}

var count = 20;
var result = addTen(count);
alert(count); // 20, 没有变化
alert(result); //30
function setName(obj){
    obj.name = "Nicholas";
}

var person = new Object();
setName(person);
alert(person.name); // "Nicholas"
function setName(obj){
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}

var person = new Object();
setName(person);
alert(person.name); // "Nicholas"

假如person是按援用通报的,那末person就会自动被修改成指向其name属性值为”Greg”的新对象。

渣滓网络

渣滓接纳

javascript具有渣滓接纳的机制,也就是说,实行环境会担任治理代码实行过程当中运用的内存。其他的不多说,我们来剖析一下函数中局部变量的平常生命周期。局部变量只在函数实行过程当中存在。而在这个过程当中,会为局部变量在栈(或堆)内存上分派响应的空间,以便存储他们的值。然后在函数中运用这些变量,直到函数终了。此时,局部变量就没有存在的必要了,因此可以开释他们所占的内存以供他们运用。如今各大浏览器通经常使用采纳的渣滓接纳有两种要领:标记消灭、援用计数。

标记消灭

这是javascript中最经常使用的渣滓接纳体式格局。当变量进入实行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永久不能开释进入环境的变量所占用的内存,因为只需实行流进入响应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

渣滓网络器在运转的时刻会给存储在内存中的一切变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量援用的标记。而在此以后再被加上标记的变量将被视为预备删除的变量,原因是环境中的变量已无法接见到这些变量了。末了。渣滓网络器完成内存消灭事情,烧毁那些带标记的值,并接纳他们所占用的内存空间。

援用计数

另一种不太罕见的渣滓接纳战略是援用计数。援用计数的寄义是跟踪纪录每一个值被援用的次数。当声清楚明了一个变量并将一个援用范例赋值给该变量时,则这个值的援用次数就是1。相反,假如包括对这个值援用的变量又取得了别的一个值,则这个值的援用次数就减1。当这个援用次数变成0时,则申明没有办法再接见这个值了,因此就可以将其所占的内存空间给收回来。如许,渣滓网络器下次再运转时,它就会开释那些援用次数为0的值所占的内存。

然则用这类要领存在着一个题目,下面来看看代码:

function problem(){
     var objA = new Object();
     var objB = new Object();
     objA.someOtherObject  = objB;
     objB.anotherObject = objA;
}

在这个例子中,objA和objB经由过程各自的属性互相援用;也就是说这两个对象的援用次数都是2。在采纳援用计数的战略中,因为函数实行以后,这两个对象都离开了作用域,函数实行完成以后,objA和objB还将会继承存在,因为他们的援用次数永久不会是0。如许的互相援用假如说很大批的存在就会致使大批的内存泄漏。

我们晓得,IE中有一部分对象并非原生JavaScript对象。比方,其BOM和DOM中的对象就是运用C++以COM(Component Object Model,组件对象)对象的情势完成的,而COM对象的渣滓接纳器就是采纳的援用计数的战略。因此,纵然IE的Javascript引擎运用标记消灭的战略来完成的,但JavaScript接见的COM对象依然是基于援用计数的战略的。说白了,只需IE中触及COM对象,就会存在轮回援用的题目。看看下面的这个简朴的例子:

var element = document.getElementById("some_element");
var myObj = new Object();
myObj.element = element;
element.someObject = myObj;

上面这个例子中,在一个DOM元素(element)与一个原生JavaScript对象(myObj)之间建立了轮回援用。个中,变量myObj有一个名为element的属性指向element;而变量element有一个名为someObject的属性回指到myObj。因为轮回援用,纵然将例子中的DOM从页面中移除,内存也永久不会接纳。

不过上面的题目也不是不能解决,我们可以手动割断他们的轮回援用。

myObj.element = null;
element.someObject = null;

内存治理

运用JavaScript编程,我们平常都不须要管内存接纳的题目,假如说想要写出高水平的代码照样有点题目值得注意。一个重要题目就是分派给WEB浏览器的可用内存一般比分派给桌面应用程序要少。如许做的目标重如果出自于平安方面的斟酌,目标是防备运转JavaScript的网页耗尽悉数体系内存致使体系崩溃。内存限定题目不仅会影响给变量分派内存,同时还会影响挪用栈以及在一个线程中可以同时实行的语句的数目。

因此,确保占用起码的内存可以让页面取得更好的机能。而优化内存占用的最好体式格局,就是实行中的代码只保留必要的数据。一旦数据不在有效,最好经由过程将其值设置为null来开释其援用——这个做法叫消除援用。这一做法适合于大多数全局变量和局部变量的属性。局部变量会在他们离开实行环境的时刻自动被消除援用,下面来看看代码:

 function createPerson(name){
       var localPerson = new Object();
       localPerson.name = name;
       return localPerson;
}
var globalPerson = createPerson("Tracy");
globalPerson = null; //手工消除援用

在这个例子中,变量globalPerson取得了createPerson()函数的返回值。在createPerson()函数内部,我们创建了一个对象并将其值赋给局部变量localPerson,然后又为局部变量增加了一个名为name 的属性。末了,当挪用这个函数的时刻,localPerson以函数值的情势返回并赋值给globalPerson。因为localPerson在createPerson()函数实行终了后就离开了实行环境,因此无需我们显现地去为他们消除援用。然则关于globalPerson而言,则须要我们不运用它的时刻手动为他消除援用。

不过,消除一个值的援用并不意味着自动接纳该值所占的内存。消除援用的真正作用是让值离开实行环境,以便渣滓网络器下次运转时将其接纳。

    原文作者:小渝人儿
    原文地址: https://segmentfault.com/a/1190000000372266
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞