首页 > 技术文章 > 变量、作用域和内存问题

niusan 2017-04-11 22:36 原文

//JavaScript高级程序设计读书笔记

1,基本类型和引用类型的值

        基本类型值:简单的数据段。有5种基本数据类型,Undefined,Null,Boolean,Number和String;它们的值保存在栈内存。基本类型是按值访问的,所以我们操作的是保存在变量中的实际值。

        引用类型值:JavaScript不能直接操作对象的内存空间,实际上是操作对象的引用,而不是实际的对象。引用类型的值是按引用访问的。查询引用类型的变量时,先从栈内存读取内存地址,然后根据地址找到堆内存中的值。

   1.1  动态的属性

      引用类型值的属性和方法可以修改:       

 

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

        基本类型值不能添加属性和方法:    

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

 

    1.2  复制变量值 

        基本类型:一个变量向另一个变量复制,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,两个变量互不影响。

        引用类型:同样会将存储在栈中的值复制一份到为新变量分配的空间中。不同的是这个值实际上就是一个指针,指针指向的是堆内存中的一个对象,两个变量实际上指的是同一个对象,改变其中的一个变量就会影响另一个。

    1.3  传递参数

    JS中所有函数的参数都是按照值传递的。

        //基本类型是按值传递的。

function add(num){
   num +=10;
   return num;
}
var count = 20;
var result = add(count);
alert(count);   //20没有变化
alert(result);   // 30

 

               //引用类型也是按值传递的,这个值是对象在内存中的地址值。    

function setName(obj){
   obj.name="Bob";
   obj = new Object();   //重新定义了一个对象,obj的值改变,obj指向一个新的对象。
   obj.name = "Greg";    //为该对象定义了一个不同值得name属性。此时和persion不是
                           同一个对象
}
var persion = new Object();
setName(persion);  //persion和obj指向同一个对象,
alert(persion.name); //  "Bob"

 

        1.4  检测类型

            typeof 操作符,确定一个变量是string,number,boolan,还是undefined。如果是对象或者null返回object;如果是函数返回function。

            var s = "Hellow World";

            console.log(typeof s);  //string 

    instanceof操作符,确定一个变量是什么类型的对象。如果检测基本类型值,返回false   

var arr =[1,2,3];
console.log(arr instanceof Array);//true

2,执行环境及作用域

    执行环境定义了变量或函数有权访问的其他数据。每个环境都有与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。//全局环境被认为是window对象,所有的全局变量和函数都作为window对象的属性和方法创建的。    

   // 函数的环境在函数执行之后,栈将其环境弹出,

内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。每个环境都可以向上搜索作用域链,任何环境都不能向下搜索作用域链而进入另一个执行环境。

   2.1  延长作用域链

//try-catch  语句  和 with 语句,这两个语句都会在作用域链的前端添加一个变量对象。使作用域链得到加长。with语句将指定对象添加到作用域链中,catch会创建一个新的变量对象。

 

function buildUrl() {
   var qs = "?debug=true";
   with(location) {   //with将location对象放添加到作用域链中,
                          //当前环境可以访问location中的变量和方法
       var url = href + qs;
   }
   return url;
}
   console.log(buildUrl());

    2.2 没有块级作用域

    

if (true) {//if语句中的变量声明会将变量添加到当前的执行环境
   var color = "blue";
}
console.log(color); //"blue"
for (var i =0; i< 10 ; i++) {
   var s = i; //即使for循环结束后,也依旧会存在于循环外部的执行环境中。
}
console.log("s的值为"+s); //s的值为9
console.log("i的值为"+i);//i的值为10

function add(num1,num2) {
   var sum = num1+num2;
   return sum;
}
var result = add(10,20); //30
console.log(sum); //由于sum不是有效变量,会报错。sum is not defined

//初始化赋值时没有使用var关键字,会将变量添加到全局环境中

function add1(sum1, sum2) {
   he = sum1 + sum2;//初始化时没有使用var关键字,he成为全局变量。
   return he;//在严格模式下,初始化未经声明的变量会导致错误。
   //不声明而直接初始化也可能导致意外,一定要先声明在使用。
}
var result1 = add1(10, 20);//30
console.log(he); //30

3,垃圾收集

    js有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。原理:找到不再继续使用的变量,然后释放其占用的内存。垃圾收集器会按照固定的时间间隔,周期性的执行这一操作。

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

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

4,管理内存

    确保占用最少的内存可以让页面获得更好的性能,而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用。解除引用这一做法适用于大多数全局变量和全局对象的属性。局部变量会在他们离开执行环境时被自动解除引用。

 

function createPerson(name) {
   var localPerson = new Object();
   localPerson.name=name;
   return localPerson;
}
var globalPerson =createPerson("Bob");
//手动解除globalPerson的引用
globalPerson=null;

 

推荐阅读