变量,作用域和内存问题 ( 4章 )
基本类型 : 保存在 栈内存 的简单数据段,这种值完全保存在内存中的一个位置。Undefined, Null, Boolean, Number, String
引用类型 : 保存在 堆内存 的对象,变量中保存的实际上只是一个指针,这个指针指向内存中的对象。Object
基本类型与引用类型的差别 :
- Object类型可以添加属性和方法,基本类型不可以。
- 复制变量,基本类型会在栈中创建空间以保存新的变量,例如 var num1 = 5; var num2 = num1; 此时,num2在内存中是独立存在的。num1和num2是相互独立的,引用类型同样也会在栈内分配内存,存放指针,指向堆内存,但是,这里要注意,并不是开辟一块新的堆内存。例如 var obj1 = new Object () ; var obj2 = obj1 ; obj1.name = “nihao”; alert( obj2.name) ; //返回结果也必然是 “nihao”,因为指向同一块堆内存。
- 传递参数,首先全部是按值传递,想内存,类似C的传值和传指针。( 虽然引用类型也是传值,但是,别望了,这只是间接的,即在函数内部接受传参的内容也只是个指针,它们都共同指向堆内存。
1 function setName(obj) { 2 obj.name = "Nicholas" ; 3 obj = new Object(); 4 obj.name = "Gerg"; 5 } 6 7 var person = new Object(); 8 setName(person); 9 alert(person.name); // "Nicholas"
上例中 : alert( person.name ) 结果是 Nicholas, 因为在传递对象后,指向内存为共同对象,此时设定 name = Nicholas, 然后,又定义了新对象,将 obj 指向了新对象,但是这对之前的对象并不产生影响。因为它们在堆内存中时真实存在的。
- 检测类型,基本类型用 typeof,使用typeof检测引用类型会返回 object,没有预期的效果,即究竟是哪个对象,instanceof(),例如 person instanceof Object 会返回结果是 true / false,这样就能判断实例是属于哪个”class”
要检测一个变量是不是基本数据类型, 最好使用 typeof 这个操作符工具, 例如:
var s = "Nicholas";
var b = true;
var i = 22;
var u ;
var n = null;
var o = new Object();
alert(typeof s); // string
alert(typeof b); // boolean
alert(typeof i); // number
alert(typeof u); // underfined
alert(typeof n); // object
alert(typeof o); // object
typeof 用来检测基本类型非常强大, 但是在检测引用类型时, 我们不仅仅想知道它是一个object, 我们还想知道它具体是那种类型的object, 这时我们需要 instance of 来判断, 如下:
result = variable instanceof constructor // 如果变量是给定引用类型的实例, 那么 instanceof 就返回 true, 例如:
alert(person instanceof Object); // true, 变量 person 是 Object 吗 ?
alert(colors instanceof Array); // 变量 colors 是 Array 吗 ?
alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗 ?
执行环境及作用域
- 执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。会与一个变量关联 例如在浏览器环境下,该对象就是 window,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。 每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中,而函数执行之后,栈将其环境弹出,把控制权返回给当前的执行环境。 当代码在一个环境中执行,会创建变量对象的一个作用域链,作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。(作用域链将是将当前环境下能访问的变量和函数联系起来,例如从本函数内部能访问的->再到外部能访问的->再到更外部能访问的,有顺序的,一级一级的 )
延长作用域链
try-catch 语句块中的catch 块 可以延长作用域链。会创建一个新的变量对象, 其中包含的是被抛出的错误对象的声明.
没有块级作用域
例如 if ( true ) { var color = “blue”; } alert( color ) ; //返回 blue,如果在其他语言中,由于在花括号内部定义的 color,花括号以外的就不再有
color变量,但是在JavaScript中,color可以继续使用。
- 在使用 var 关键字声明变量时,这个变量将被自动添加到距离最近的可用环境中。
- 搜索标识符(函数名,变量名等等)要根据作用域链逐级搜索。向上,如果局部旱井中存在着同名标识符,就不会使用位于父环境中的标识符
垃圾收集
JavaScript有自动回收内存机制, 但是, 优化内存还是很有必要 , 最好的办法是 ,执行中的代码只保存必要的数据. 一旦数据不再有用, 最好通过将其值设置为null来释放其引用-这个做法叫做解除引用.这一做法适用于大多数全局变量和全局对象,局部变量没关系(会自动被解除) , 堆中的自动消除, 只要将连接该堆的指针设置成 null , 当系统检测到没有指针连接该堆时, 就会将该堆的内存释放。
将指向堆内存的指针(对象) 设置成 null 后,系统会自动释放堆内存的空间,对应C的内容就是 free(p) .
目前一般的语言, 在堆内存中, 都是这个办法, "引用计数" 即确认这块堆内存是否还有指针在连接, 如果没有就释放这块堆内存.