js中的变量虽然不区分类型,可是实际上Ecmascript包含两种类型,基本类型和引用类型.java
基本类型有5种:Undefined,Null,Boolean,Number,String,基本类型是按值访问的,由于能够操做保存在变量中的实际的值。算法
引用类型的值是保存在内存中的对象。与其余语言不一样,JavaScript 不容许直接访问内存中的位置,也就是说不能直接操做对象的内存空间。在操做对象时,其实是在操做对象的引用(也称为句柄)而不是实际的对象.浏览器
js中函数参数的传递只有一种:值传递.请看下面的例子:bash
function setName (obj) {
obj.name = 'hello';
}
var p = new Object();
setName(p);
console.log(p.name); // hello复制代码
以上代码中建立一个对象,并将其保存在了变量p中。而后,这个变量被传递到
setName()
函数中以后就被复制给了obj
。在这个函数内部,obj
和p
引用的是同一个对象。换句话说,即便这个变量是按值传递的,obj
也会按引用来访问同一个对象。因而,当在函数内部为obj
添加name
属性后,函数外部的p
也将有所反映;由于p
指向的对象在堆内存中只有一个,并且是全局对象。有不少开发人员错误地认为:在局部做用域中修改的对象会在全局做用域中反映出来,就说明参数是按引用传递的。为了证实对象是按值传递的,咱们再看一看下面这个通过修改的例子:函数
function setName (obj) {
obj.name = 'hello';
obj = new Object();
obj.name = 'gray';
}
var p = new Object();
setName(p);
console.log(p.name); // hello复制代码
这个例子与前一个例子的惟一区别,就是在setName()函数中添加了两行代码:一行代码为obj从新定义了一个对象,另外一行代码为该对象定义了一个带有不一样值的name属性。在把p传递给setName()后,其name属性被设置为"hello"。而后,又将一个新对象赋给变量obj,同时将其name属性设置为"gray"。若是p是按引用传递的,那么p就会自动被修改成指向其name属性值为"gray"的新对象。可是,当接下来再访问person.name时,显示的值仍然是"hello"。这说明:即便在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后当即被销毁。ui
typeof
运算符在检测基本类型的时候很是有用,可以识别出:string,number,boolean,undefined,可是对null
和对象进行此运算获得的都是'object'
.此操做符对于检测对象来讲没有用(由于咱们一般是想知道某值是否是对象,咱们想知道它是什么的实例).spa
对于检测对象,咱们有instanceof
运算符:指针
result = variable instanceof constructor复制代码
若是对基本数据类型进行instanceof
运算,获得的结果将是false
,由于基本类型不是对象.code
垃圾收集机制其实很是简单,周期性地找出再也不继续使用的变量,释放其内存.标识无用变量的策略有如下的2种方式:对象
绝大多数浏览器采用的垃圾收集机制.
当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,由于只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
可使用任何方式来标记变量。好比,能够经过翻转某个特殊的位来记录一个变量什么时候进入环境,或者使用一个“进入环境的”变量列表及一个“离开环境的”变量列表来跟踪哪一个变量发生了变化。说到底,如何标记变量其实并不重要,关键在于采起什么策略.
垃圾收集器在运行的时候会给存储在内存中的全部变量都加上标记(固然,可使用任何标记方式)。而后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此以后再被加上标记的变量将被视为准备删除的变量,缘由是环境中的变量已经没法访问到这些变量了。最后,垃圾收集器完成内存清除工做,销毁那些带标记的值并回收它们所占用的内存空间。
跟踪记录每一个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。若是同一个值又被赋给另外一个变量,则该值的引用次数加1。相反,若是包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,于是就能够将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
这种方式存在一个严重的问题循环引用.循环引用指的是对象 A 中包含一个指向对象 B 的指针,而对象 B 中也包含一个指向对象 A 的引用。
function problem() {
var objectA = new Object();
var objectB = new Object();
objectA.someOtherObject = objectB;
objectB.anotherObject = objectA;
}复制代码
在这个例子中, objectA 和 objectB 经过各自的属性相互引用;也就是说,这两个对象的引用次数都是2。在采用标记清除策略的实现中,因为函数执行以后,这两个对象都离开了做用域,所以这种相互引用不是个问题。但在采用引用计数策略的实现中,当函数执行完毕后,objectA 和 objectB 还将继续存在,由于它们的引用次数永远不会是0。假如这个函数被重复屡次调用,就会致使大量内存得不到回收。为此,Netscape 在 Navigator 4.0中放弃了引用计数方式,转而采用标记清除来实现其垃圾收集机制。但是,引用计数致使的麻烦并未就此终结。
IE 中有一部分对象并非原生JavaScript对象。例如,其BOM和DOM中的对象就是使用C++以COM(Component Object Model,组件对象模型)对象的形式实现的,而 COM 对象的垃圾收集机制采用的就是引用计数策略。所以,即便IE的JavaScript引擎是使用标记清除策略来实现的,但JavaScript访问的COM对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。为了解决上述问题,IE9 把 BOM 和 DOM 对象都转换成了真正的 JavaScript 对象。这样,就避免了两种垃圾收集算法并存致使的问题,也消除了常见的内存泄漏现象。