基本类型指的是简单的数据段,引用类型指那些可能由多个值构成的对象。引用类型的值是保存在内存中的对象。在实际操作中,实际上是在操作对象的引用而不是实际的对象,因此引用类型的值是按引用访问的
s所有函数的参数都是按值传递的,也就是把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而访问类型值的传递则如同应用类型变量的复制一样。即:在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数,或者说arguments对象中的一个元素);向函数传递引用类型的变量时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化可以反映在函数的外部
typeof是确定一个变量时字符串、数值、布尔值,还是undefined的最佳工具。但是在检测引用类型的值时需要instanceof操作符
执行环境是js中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
执行环境的类型只有两种,全局和局部(函数)
每个执行环境都有一个与之关联的变量对象,执行环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但是解析器在处理数据时会在后台使用它。
全局执行环境是最外围的一个执行环境。根据ES实现的宿主环境不同,表示执行环境的对象也不同,在Web浏览器中,全局执行环境被认为是window对象,因此,所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。全局执行环境直到应用程序退出,如关闭网页或浏览器时才会被销毁
每个function都有自己的执行环境,当执行环境进入一个函数时,函数的环境就会被推入一个环境栈中,而在函数执行结束后,栈将其环境弹出,把控制权返回给当前的环境。ES中的执行流由这样的机制控制着
当代码在一个执行环境中执行时会创建变量对象的一个作用域链,作用域链的用途:保证对执行环境有权访问的所有变量和函数可以有序访问。
全局执行环境的变量对象始终都是作用域链的最后一个对象
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问color,anotherColor,tempColor
}
// 这里可以访问anotherColor,color,不能访问tempColor
swapColors();
}
// 这里只能访问color
changeColor();
解释:标识符解析是沿着作用域链一级一级第搜索标识符的过程。而且,搜索过程始终从作用域链的前端开始,然后逐级第向后回溯,知道找到标识符为止
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间的联系是线性的,有次序的。每个函数都可以向上搜索作用域链,来查询变量和函数名,但任何环境都不能向下搜索作用域链来进入另一个执行环境
有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象在代码执行后被移除,从而实现作用域链的加长。
这样的语句有:
1.try-catch语句的catch块
2.with语句
function buildUrl(){
var query = "?debug=true";
with(location){
var url = href + query;
// 在with语句中引用href实际应用的是location.href,
//可以在当前执行环境中找到
}
//with内部定义的url变量成了函数执行环境的一部分,所以可以在
//最后作为函数的返回值
return url;
}
if (true) {
var color = "blue";
}
console.log(color);// "blue"
在使用for循环时一定要牢记这一差异:
for (var i = 0; i < 10; i++) { } console.log(i); //10
使用var声明的变量会自动被添加到最接近的环境中
注意:在编写js代码时,不声明而直接初始化变量是一个常见的错误做法,所以初始化变量之前一定要先声明。 在严格模式下,初始化未经声明的变量会导致错误
通过搜索来确定变标识符实际代表什么。搜索的过程从作用域链的前端开始,向上逐级产讯与给定名字匹配的标识符。如果在局部环境中找到了变量标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿着作用域链继续向上搜索。一直追溯到全局环境的变量对象
注意:变量查询时有代价的,访问局部变量要比访问全局变量更快,不用向上搜索作用域链。 JS引擎在优化标识符查询方面做得不错,因此这个差别在将来恐怕也可以忽略不计了
JS具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存,在编写js代码时开发人员不用关心内存使用情况,所需的内存分配以及无用内存的回收都完全实现了自动管理。
垃圾收集机制的原理大概是:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照国定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作
局部变量只在函数执行的过程中存在,这个过程中,回味局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值,然后在函数中使用这些变量,直到函数执行结束。此时,局部变量没有存在的必要了,因此可以释放它们的内存,这种情况下,很容易判断变量是否还有存在的必要。但是并非所有情况都这么容易得出结论。垃圾收集器必须跟踪哪个变量没用,对于不再有用的变量打上标记,以备将来回收期占用的内存
标识无用变量的策略有两种:标记清除和引用计数
常用的垃圾收集方式是标记清除,IE,Firefox,Opera,Safari,Chrome的JS实现使用的都市标记清除式的垃圾收集策略或类似的策略,只不过是垃圾收集的时间间隔互有不同。引用计数垃圾收集存在循环引用的问题,一般都慎用
循环引用 就是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向A的指针。
IE的JavaScript引擎是使用标记清除策略来实现的,但是其BOM和DOM中的对象就是C++以COM(组件对象模型)对象的形式实现的,COM对象的垃圾收集机制采用引用计数类型实现。因此,只要在IE中涉及COM对象,就会存在循环引用的问题
××××
为了避免循环引用的问题,最好是在不使用对象的时候手工断开原生js对象与DOM元素之间的连接。
obj = null;
将变量设置为null,意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值回收它们的内存
这个问题在IE9得到了解决,IE9把DOM和BOM对象都换成了真正的JavaScript对象。这样就避免了两种垃圾收集算法并存导致的问题,也消除了常见内存泄漏现象
确定垃圾收集器的时间间隔是一个非常重要的问题。
IE的垃圾收集器是根据内存分配量运行的,即有几个方面的临界值,一旦超出其中一个,垃圾收集器就会运行。临界值一开始是静态分配的,这就存在着问题,如果一个脚本中包含着很多变量,那么该脚本很可能在其生命周期中一直保有那么多的变量。这样一来垃圾收集其就会频繁地运行,引发严重的性能问题
IE7的引擎优化工作方式是:临界值被调整为可动态修正。极大第提高了IE在运行包含大量js代码的页面时的性能。
有的浏览器中可以触发垃圾收集古城,但是尽量不要使用。IE中,调用window.CollectGarhage().OPera7及更高版本中调用window.opera.collect()也会启动来及收集例程
使用类似于Java这样的语言编写程序,一般不必操心内存管理的问题。但是js面临的问题不同于一般,最主要的一个问题就是:分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少。(为了防止运行js的网页耗尽全部系统内存而导致系统崩溃)。内存限制不仅影响给变量分配内存,同时也影响调用栈以及在一个线程中能够同时执行的语句数量,因此确保占用最少的内存可以让页面获得更好的性能。
优化内存占用的最佳方式是解除引用,就是为执行的代码只保存必要的数据,一旦数据不再有用,最好通过将其设置为null来释放其引用
注意:
解除一个值的引用并不意味着自动回收该值所占用的内存,解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行将其回收
这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动解除引用。