变量、作用域和内存问题

javascript的变量和其他语言的变量有很大区别。

基本类型和引用类型的值

ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值就是简单的数据段,而引用类型值则是那些对象。

javascript不允许直接访问内存中的位置

  • 动态的属性
    对于引用类型值,我们可以为其添加属性和方法,也可以删除。但是基本类型值是不能这么用的。
var person = new Object();
person.name = "tony";
console.log(person.name);
  • 复制变量值
    基本类型值就是那样。
    引用类型值的复制实际上是复制了指针,也就是说两个变量的指针都指向了同一个对象,所以当改变了一个对象的值以后,另一个对象的值也会发生改变。
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert("obj2.name"); // Nicholas
  • 传递参数
    ECMAScript中所有函数的参数都是按值传递。
    传递基本类型的值时,被传递的值会复制给一个局部变量,因此函数内的改变不会影响外面的值。
    但是引用类型的值会改变外面的值。但是还是要注意,按值传递。会改变是因为obj和person引用的是同一个对象。
    其实这一点参照复制就好。
// 这个例子可以证明按值传递。
function setName(obj) {
  obj.name = "Nicholas";
  // 实际上,当在函数内部重写 obj 时,这个引用变量引用的就是一个局部对象。
  // 这个对象会在函数执行完毕以后立即销毁。
  obj = new Object();  
  obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); // "Nicholas",若是按引用传递,则会显示"Greg"
  • 检测类型
    使用以前的typeof可以检测一个变量是不是基本类型。
    若是想要知道某个值是个什么类型的对象,可以使用 instanceof。
    使用方法:result = variable instanceof constructor 前面是要检测的实例,后面是你想要问的对象。
console.log(person instanceof Object); // true

使用 typeof 操作符检测函数时,会返回function,而以前也说过,老的版本的浏览器检测正则表达式也会返回function。

执行环境及作用域

执行环境是javascript中最为重要的一个概念,执行环境定义了变量或者环境有权访问的其他数据。
全局执行环境被认为是windows对象。

作用域链

当代码在一个环境中执行,会创建变量对象的一个作用域链。作用域链的作用保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端始终是当前代码的所在环境的变量对象。下一个则是外部环境。全局变量是作用域链的最后一个对象。

通过以下代码分析

var color = "blue";

function changeColor(){
  if (color === "blue"){
    color = "red";
  } else {
    color = "blue";
  }
}
changeColor();
alert("color is now" + color);

在这个例子当中,函数changeColor的作用域包含两个对象,自己的arguments,还有一个全局环境的变量对象。
局部作用域定义的变量可以在局部环境中与全局变量互换使用。

var color = "blue";

function chageColor() {
  var anotherColor = "red";
  function swapColors() {
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
    // 在这里可以访问color,tempColor,anotherColor
  }
  // 这里可以访问color,anotherColor但不能访问tempColor
  swapColors();
}
changeColor();

内部环境变量可以通过作用域链访问外部环境的变量,但是外部的不能这样对内部有什么想法。

  • 延长作用域链
    有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。
    ~ try-catch 语句的 catch 块
    ~ with 语句
  • 没有块级作用域
    javascript中没有块级作用域。
    也就是说像这样的情况很正常
for (var i = 10; i < 10; i++) {
  doSomething(i);
}
alert(i); // 10
声明变量

利用 var 声明的变量会被自动添加到最近的环境中,(不用var声明的变量全局,不推荐)

查询标识符

某个环境为了读取或者写入而引用一个标识符时,必须通过搜索来确定标识符实际代表什么,搜索过程从作用域前端开始。所以很正常的,局部和父亲同名的标识符,肯定会使用局部的标识符。

垃圾收集

javascript具有垃圾收集机制。
原理:找到那些不在继续使用的变量,然后释放其占用的内存。这项工作按照固定时间间隔周期性执行。
局部变量只会在函数执行的过程中存在。在这个过程中,为改变量在栈中分配空间,以便存储。
垃圾收集器需要标记哪些变量需要存在,标记的方法有两种

  • 标记清除
    javascript中最常用的垃圾收集方式,标记清除
    将离开环境的变量标记为“离开环境”。如何标记的不是很重要。
  • 引用计数
    跟踪记录每个值被引用的次数。
    声明一个变量并将一个引用类型值赋给该变量时,引用次数+1;
    同一个值又被赋给另一个变量,引用次数+1;
    但是包含对这个值引用的变量又取得了另一个值,-1;
    值为0时。回收。
    但是这个办法无法解决循环引用的问题
function problem() {
  var objA = new Object();
  var objB = new Object();
  objA.someOtherObject = objB;
  objB.anotherObject = objA;
}

这种情况,内存永远不会得到回收。
COM对象就是使用计数策略。

var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.somObject = myObject;

为了避免像上述代码的问题,推荐在不适用他们的时候断开原生javascript对象与DOM对象的连接,比如

myObject.element = null;
element.someObject = null;

你可能感兴趣的:(变量、作用域和内存问题)