根据ECMA-262的定义,javascript的变量和其他语言的变量有很大区别。javascript松散变量的本质,决定了它只是在特定时间内保存着特定值的名字而已。由于不存在某个变量必须要保存某个特定类型的值的规则,一个变量的值和类型可以在脚本的生命周期内改变。
ECMAScript变量可能包含两种不同数据类型的值:基本类型和引用类型。
基本类型值指的是简单的数据段,引用类型值指的是那些可能由多个值组成的对象。
基本数据类型包括:Undefined Null Boolean Number String。是按值访问的。
引用类型的值是保存在内存中的对象。和其它语言不同,JavaScript不允许直接访问内存中的位置,也就是说,不能直接操作对象的存储空间。在操作对象时,实际上是在操作对象的引用而不是对象。
1、动态的属性
只能为引用类型添加属性,基本类型添加的属性我们访问不到:
var person = new Object();
person.name = "Nicholas";
alert(person.name); //"Nicholas"
var name = "Nicholas";
name.age = 27;
alert(name.age); //undefined
2、复制变量值
引用类型的复制只是复制了地址,相当于多了一个指向这块内存的引用。
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"
3、传递参数
ECMAScript中所有函数的参数都是值传递的。
在向参数传递基本类型的值时,被传递的值会赋值给一个局部变量。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化,会反映到函数外面。
例子:
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20
alert(result); //30
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
//证明值传递
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"
4、检测类型
var s = "Nicholas";
var b = true;
var i = 22;
var u;
var n = null;
var o = new Object();
alert(typeof s);
alert(typeof i);
alert(typeof b);
alert(typeof u);
alert(typeof n);
alert(typeof o);
// 输出结果
//string
//number
//boolean
//undefined
//object
//object
typeof是检测基本数据类型的好帮手,但是对于引用类型,我们应该使用instanceof方法:
alert(person instanceof Object);
alert(colors instanceof Array);
alert(pattern instanceof RegExp);
当然,如果用instance来检测基本类型,则始终会返回false。
执行环境,是JS中很重要的一个概念。执行环境定义了函数或者变量有权访问的其它数据。
在web浏览器中,全局执行环境被认为是window对象。
某个执行环境中的所有代码运行完毕后,该环境被销毁,其中的代码和函数也都被销毁(全局环境则当应用程序退出时才被销毁)。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境会被推入一个环境栈中。函数执行完毕后,环境从栈中弹出,把控制权返还给之前的环境。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的作用,是保证对执行环境有权访问的变量和函数的有序访问。
标识符解析是沿着作用域链一级一级的搜索标识符的过程。搜索过程从作用域链的最前端开始(当前执行环境),直到找到标识符为止,如果找不到,则通常会报错。
例子:
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//可访问 color anotherColor tempColor
}
//可访问color anotherColor,不可访问tempColor
swapColors();
}
//只能访问color
changeColor();
1、延长作用域链
当执行流进入下面任意一个语句时,作用域链就会得到加长。
1、try-catch 语句的catch块
2、with语句
function buildUrl() {
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}
2、没有块级作用域
在其它语言中,由花括号包裹的部分就是一个独立的执行环境,括号内的语句执行完毕后,里面定义的变量会被销毁。然而,JS没有块级作用域的概念,就导致了下面这样的代码也不会报错。
if (true) {
var color = "blue";
}
alert(color); //"blue"
for (var i=0; i < 10; i++){
doSomething(i);
}
alert(i); //10
1、声明变量
使用var声明的变量会被自动加入到最近的执行环境中去。如果初始化时没有使用var声明,则改变量就会被自动加入到全局变量中。
2、查询标识符
查询顺序是沿着作用域链向上搜索:
// 实例代码1
var color = "blue";
function getColor(){
return color;
}
alert(getColor()); //"blue"
// 实例代码2
var color = "blue";
function getColor(){
var color = "red";
return color;
}
alert(getColor()); //"red"
JS具有自动垃圾清理的机制,也就是说,执行环境会负责管理代码执行过程中的内存。这种垃圾收集的机制的原理其实很简单:找出那些不再使用的变量,然后释放掉它们占用的内存。为此,垃圾收集器会周期性的执行这个操作。
1、标记清除
JS最常用的垃圾收集方式就是标记清除。当一个变量进入环境时,就将变量标记为“进入环境”。当变量离开环境时,则标记为“离开”。
垃圾收集器在运行的过程中会给所有内存中的变量都加上标记,然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后,再被加上标记的变量将被视为准备删除的变量,因为环境中的变量已经无法访问到这些变量了。最后,垃圾处理器完成内存清理工作,销毁那些带标记的值,并回收它们的存储空间。
2、引用计数
引用计数是一种不太常见的垃圾收集策略。引用计数的策略是跟踪记录每个值被引用的次数。当某个变量的引用计数为0时,表示该变量已经无法被访问了,则改变量可以被回收。
3、管理内存
使用具备垃圾收集机制的语言编写程序,开发人员一般不用操心内存管理的问题。但是,确保使用最少的内存可以让页面获得更好的性能。优化内存占用的最好方式,就是为执行中的代码只保存最少的数据。一旦数据不再使用,最好通过将其设置为null来释放其引用。局部变量会在它们离开执行环境后自动被解除引用。
function createPerson(name){
var localPerson = new Object();
localPerson.name = name;
return localPerson;
}
var globalPerson = createPerson("Nicholas");
// 解除globalPerson的引用
globalPerson = null;