JavaScript笔记:变量、作用域和内存问题

来源:互联网 发布:网络效应2017试题答案 编辑:程序博客网 时间:2024/05/22 02:23

根据ECMA-262的定义,javascript的变量和其他语言的变量有很大区别。javascript松散变量的本质,决定了它只是在特定时间内保存着特定值的名字而已。由于不存在某个变量必须要保存某个特定类型的值的规则,一个变量的值和类型可以在脚本的生命周期内改变。

1、基本类型和引用类型的值

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); //20alert(result); //30function 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。

2、执行环境和作用域

执行环境,是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、查询标识符

查询顺序是沿着作用域链向上搜索:

// 实例代码1var color = "blue";function getColor(){    return color;}alert(getColor());  //"blue"// 实例代码2var color = "blue";function getColor(){    var color = "red";    return color;}alert(getColor());  //"red"

3、垃圾收集

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;
0 0
原创粉丝点击