Skip to content

Latest commit

 

History

History
101 lines (56 loc) · 4.34 KB

004-variables-scope.md

File metadata and controls

101 lines (56 loc) · 4.34 KB

JavaScript 变量、作用域、内存问题

一、变量:基础类型和引用类型的值

1、动态的属性

定义基本类型的值和引用类型值的方式时类似的:创建一个变量并为该变量赋值。

  • 引用类型:我们可以为添加/修改/删除属性和方法。
var person = new Object();
person.name = "Wjh";
alert(person.name);  // "Wjh"
  • 基本类型:不能添加属性(虽然不会报错)。
var name = "Wjh";
name.age = 27;
alert(name.age);  // undefined

2、复制变量值

这里就出现了我们熟悉的知识点:对象引用的概念。 基本类型:复制是值复制(复制的是值的副本); 引用类型:复制是引用复制(复制的是引用指针的副本,实际的值还是同一个);

3、传递参数

ECMAScript中所有的函数的参数都是按值传递的。 把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,引用类型的传递,则如同引用类型变量的复制一样。

function setName(obj){
  obj.name = "Wjh";
}

var person = new Object();
setName(person);
alert(person.name); // "Wjh"

以上代码创建了一个对象,并将其保存在了变量person中。然后,这个对象被传递到setName()函数中之后就被复制给了obj。在这个函数内部,obj和person引用的是同一个对象。换句话说,即使这个对象是按值传递的,obj也会按引用来访问同一个对象。于是函数内部为obj添加name属性后,函数外部的person也将有所反映;因为person指向的对象在堆内存中也只有一个,而且是全局对象。有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,我们看一个例子:

function setName(obj){
  obj.name = "Wjh";
  obj = new Object();
  obj.name = "Nicholas";
}

var person = new Object();
setName(person);
alert(person.name); // "Wjh"

这个例子与前一个例子的唯一的区别,就是在setName()函数中添加了两行代码:一行代码为obj重新定义了一个对象,另一行代码为该对象定义了一个带有不同值的name属性。在把person传递给setName()后,其name属性被设置为“Wjh”。然后,又将一个新对象赋给变量obj,同时将其name属性设置为“Nicholas”。如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为“Wjh”的新对象。但是,当接下来在访问person.name时,显示的值仍然是“Wjh”。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持未变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即销毁。

可以把 ECMAScript 函数的参数想象成局部变量。

4、检测类型

在检测基本数据类型时 typeof 是非常得力的助手,但在检测引用类型的时候,这个操作符就用处不大了。这就用到了下一个操作符:instanceof。

result = variable instanceof constructor

二、垃圾收集

JavaScript 是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题。可以对JavaScript的垃圾收集例程做如下总结。

  • 离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除。
  • “标记清除”是目前主流的垃圾手机算法,这种算法的思想是给当前不使用的值加上标记,然后在回收其内存。
  • 另一种垃圾收集算法是“引用计数“,这种算法的思想是跟踪记录所有值引用的次数。JavaScript引擎目前都不再使用这种算法;但在IE中访问非原生JavaScript对象(如DOM元素)时,这种算法仍然可能会导致问题。
  • 当代码中存在循环引用现象时,”引用计数“ 算法就会导致问题。
  • 解除变量的引用不仅有助于消除循环引用现象,而且对垃圾收集也有好处。为了确保有效地回收内存,应该及时解除不再使用的全局对象、全局对象属性以及循环引用变量的引用。