一.前言
本章主要介绍的是两种类型的区别和引用方法,这个很重要,引用方法的特殊性是必须掌握的。还有执行环境与作用域,帮助我们理解代码的执行过程,最后的垃圾回收做了解即可,用于面试。
二.基本类型和引用类型
-
引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript不允许直接访问内存中的位置,就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。
-
不能给基本类型的值添加属性,虽然不会报错。
var name = 'nico' name.age = 27 alert(name.age) //undefined
-
复制变量值
从一个变量向另一个变量复制的基本类型值和引用类型值是不同的。
①基本类型值var num1 = 5; var num2 = num1
num1的值是5,执行第二行代码时会在变量num2上创建一个新值,然后把num1的值5复制到新变量分配的位置上。
num1的5和num2的5是完全独立的,此后的任何操作都不会互相影响。
②引用类型值var obj1 = new Object(); var obj2 = obj1; obj1.name = 'nico' alert(obj2.name) //'nico'
执行第二行代码时,也会将存储在obj1中的值复制一份到obj2分配的空间中。但是这个值实际上只是一个指针,指针指向的是存储在堆中的一个对象。**复制操作结束后,两个变量实际上指向的是同一个变量。**所以obj2.name = obj1.name
-
传递参数
ECMAScript中所有函数的参数都是按值传递的。基本类型值的传递如同基本类型变量的复制一样,引用类型值的传递如同引用类型变量的复制一样。
变量的访问有按值和按引用两种,但是函数参数的传递只能按值传递。基本类型值的参数传递好理解,内部值的变化不会影响外部基本类型变量的变化。
但是如何证明对象时按值传递的。function setName(obj){ obj.name = 'nico' obj = new object() obj.name = 'love' } var person = new Object(); setName(person); alert(person.name) //‘nico’
如果是按引用传递,那么person就会自动被修改为name指向‘love’的新对象,然而这里新创建的对象只是个局部对象而已,在函数执行完毕后立即被销毁。
-
检测类型
①监测一个变量是不是基本数据类型,最好的工具是typeof()
②如果变量是给定引用类型,则instanceof会返回truealert(a instanceof Object) //a是Object嘛 alert(b instanceof Array) //a是Array嘛 alert(c instanceof RegExp) //a是RegExp嘛
三.执行环境及作用域
-
在web浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。
-
每个函数都有自己的执行环境,执行流进入一个函数之后,函数的环境会被推入一个环境栈中,函数执行之后,栈就会将环境推出,把控制权返回给之前的执行环境。
-
代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的前端,始终是当前执行代码所在环境的变量对象。如果这个环境是函数,则函数的活动对象作为变量对象。
什么是活动对象呢:最开始只包含一个变量,即arguments对象(在window中是不存在的)。
作用域链中下一个变量对象是来自外部环境,下下个变量是来自下一个外部环境。作用域链中的最后一个对象时全局执行的变量对象。 -
内部环境可以根据作用域链访问外部环境,但外部环境不能访问内部环境中的任何变量和函数。
-
延长作用域链。虽然执行环境的类型只有两种——全局和局部(函数),但是有些语句可以在作用域的前端临时增加一个变量对象。该变量对象会在代码执行后被移除。
①trt-catch语句的catch块。
②with语句。
function buildUrl(){
var qs = 'a'
with(location){
var url = href+qs
}
return url
}
在此,with语句接受的是location对象,因此其变量对象中包含了location对象中的所有属性和方法,这个变量对象被添加到了作用于链的前端。在with内部定义了一个名字是url变量,因此url就成了函数执行环境的一部分,可以被返回。
6.没有块级作用域
JavaScript的花括号之间代码块没有自己的作用域。比如说for循环定义的变量i在循环结束后仍然保存在循环外部的执行环境中。
①声明变量
使用var声明的变量会自动添加到最接近的环境中。如果初始化时没有使用var声明,该变量会自动被添加到全局环境。
function add(num1,num2){
sum = num1 + num2
return sum
}
var result = add(10,20)
alert(sum) //30
如上,没有使用var声明sum,所以在全局环境中可以接收到。
不过在严格模式下,不声明变量就初始化会导致错误。
7.查询标识符
在某个环境为了读取或写入一个标识符,搜索过程从作用域链前端开始,向上逐级查询与给定名字匹配的标识符。搜素过程一直追溯到全局环境的变量对象。
四.垃圾收集
1. 标记清除
垃圾回收器会在运行的时候给存储在内存中的所有变量都加上标记。然后会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在这之后还是被加上标记的变量就视为准备删除的变量。
2.引用计数
跟踪记录每一个值被引用的次数。**声明变量并将引用类型值赋给变量时,这个值的引用次数是1,又赋值给另一个变量,则引用次数加1。如果包含这个值的引用又取到了另外一个值,则引用次数减一。**当这个值的引用次数为0,则会被回收。
但是IE中有一部分对象不是原生的JavaScript对象,比如bom,dom是使用c++以com对象的形式实现的。会存在com对象循环引用的问题(详情见书)。不过IE9把bom和dom对象都转换成了真正的JavaScript对象,就不会有两种垃圾收集算法并存导致的问题,也消除了常见的内存泄漏现象。