[JavaScript高级程序设计]第4章 变量、作用域和内存问题

目录

变量

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

1.1 动态属性

1.2 复制变量值

3.传递参数

4.检测类型

执行环境及作用域

1.延长作用域链

2.没有块级作用域

2.1 声明变量

2.2 查询标识符

垃圾收集

1.标记清除

2.引用计数

3.性能问题

4.管理内存


变量

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

ECMAScript包含两种不同数据类型的值:基本类型值(简单的数据段)和引用类型值(由多个值构成的对象)。

基本类型是按值访问的,引用类型是按引用访问的。

1.1 动态属性

定义基本类型和引用类型的方式是类似的:创建一个变量并为该变量赋值。但是当这个只保存到变量中以后,对不同类型值可以执行的操作是大相径庭的,对于引用类型,我们可以为其添加属性和方法,也可以修改和删除其属性和方法,对基本类型值添加属性没有作用。

var person = new Object();
person.name = "Nicholas";
console.log(person.name);    //Nicholas

var name = "Nicholas";
name.age = 29;
console.log(name.age);       //undefined

1.2 复制变量值

1)基本类型

从一个变量向另一个变量赋值基本类型值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

var num1 = 5;
num2 = num1;

num2中的5与num1中的5是完全独立的,这两个变量参加任何操作都不会相互影响

2)引用类型

从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到新变量的分配空间中,但这个值实际上是一个指针,指向存储在堆中的一个对象。

var obj1 = new Object();
var obj2 = obj1;
obj2.name = "Nicholas";
console.log(obj1.name);    //"Nicholas"

obj1和obj2指向了同一个对象,其中一个对其属性进行修改就会立刻反应到另一个实例上。

3.传递参数

ECMAScript中所有的参数传递都是按值传递的,就像基本类型的复制一样,函数参数在函数内发生的变化不会影响函数外部传入的变量值。但是对于传入引用类型时,即使这个变量是按值传递的,函数参数也会按引用来访问同一个对象。

function addTen(num){
    return num += 10;
}
var count = 20;
var result = addTen(num);
console.log(result);    //30
console.log(count);     //20


function setName(obj){
    obj.name = 'Nicholas';
}
var person = new Object();
setName(person);
console.log(person.name);    //'Nicholas'

4.检测类型

  • 检测一个变量是不是基本类型,使用typeof操作符
  • 监测一个引用类型的值是什么类型的对象时,使用instanceof
    语法:var result = variable instanceof canstrutor
    var arr = new Array();
    arr instanceof Array;    //true

执行环境及作用域

执行环境是JavaScript中的一个重要概念,定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个变量对象,环境中定义的对象和函数都保存在这个变量对象中。

每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。在函数执行之后,栈将其环境弹出,把控制权还给之前的执行环境。

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的一个用途是保证对执行环境有权访问的变量和函数的有序访问。作用域链的前端始终都是当前执行的代码所在环境的变量对象。

标识符解析是沿着作用域链一级一级地搜索标识符的过程,搜索过程从作用域链的前端开始,然后逐级地向后回溯,知道找到标识符为止。(若找不到,通常会导致错误发生)

1.延长作用域链

有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。在以下两种情况下会发生这种现象:

  • with语句
  • try-catch语句

对with语句来说,会将指定的对象添加到作用域链中;对catch语句来说,会创建一个新的变量对象,其中包含的就是被泡出错误对象的声明。

2.没有块级作用域

JavaScript中是没有块级作用域的(ES5)(ES6中有块级作用域)。

2.1 声明变量

使用var声明的变量会自动被添加到最接近的环境中,在函数内部,最接近的环境就是函数的局部环境,在with语句中最接近的就是函数环境。在初始化变量是没有使用var则会被添加到全局变量中。

2.2 查询标识符

搜索过程会从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。否则,继续沿作用域链向上搜索。如果局部环境中存在同名标识符,则不会使用父环境中的标识符。
 

垃圾收集

JavaScript具有自动垃圾收集机制,即执行环境会负责管理代码执行过程中使用的内存。在编写JavaScript代码时,所需的内存分配和无用的内存回收都实现了自动管理。这种垃圾收集机制的原理很简单:找出那些不再使用的变量,释放其占有的内存。即,垃圾收集机制会按照固定的时间间隔,周期性地执行这一操作。

1.标记清除

JavaScript中最常用的垃圾收集方式是标记清除。垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后他会去掉环境中的变量以及被环境中的变量引用的变量的标记,而在这之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问这些变量了。

2.引用计数

跟踪记录每个值被引用的次数。

问题:循环引用。

3.性能问题

IE7的JavaScript引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配,字面量和数组元素的临界值被调整为动态分配。IE7中各临界值在初始时与IE6相等(256个变量,4096个对象获数组字面量和数组元素,64KB字符串)。若垃圾收集例程回收的内存分配量低于15%,则临界值加倍;若垃圾收集例程回收了85%的内存分配量,则将各临界值调整回默认值。

4.管理内存

分配给web浏览器的可用内存数量要比分配给桌面应用程序的少,以防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,还会影响调用栈和一个线程中能够同时执行的语句数量。

猜你喜欢

转载自blog.csdn.net/liwen_ye/article/details/81197993
今日推荐