JavaScript读书笔记04

引用类型

在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称作是,但其实这跟传统的面向对象的语言是不一样的,它并不支持接口等基本功能。对引用类型更确切的称呼是对象定义,因为它就是在描述一类对象所具有的属性和方法。

Object类型

前面我们提到变量有5种基本类型(Undefined, Null, Boolean, Number, String)和一种复杂类型,这种复杂类型就是Object类型。Object类型就是目前为止我们用到的最多的引用类型,也是其实所有引用类型的基础。

引用类型的值有个专有的名词,叫做对象,也是这个引用类型的一个实例。创建一个新的对象使用new操作符,后跟一个构造函数来创建。构造函数本身就是一个函数,只是这个函数的目的就是用来创建新对象:

var person = new Object();	//person就是一个新的对象,也是Object类型的一个实例

基本类型和引用类型的区别

上面提到引用类型有一些专业的术语,像对象,实例等等。除了这些表述的差异,本质上也是有着很大的差异的。

  • 对基本类型,是按值访问,直接操作保存在内存中的值;
  • 对引用类型,是按引用访问,对对象的操作其实操作的对象的引用。

会在后面的例子中理解这里的区别。

动态属性

基本类型和引用类型的另外的一个很大的区别是,可以进行的操作是不一样的。对引用类型的值,我们可以为其动态的添加、改变和删除其属性和方法:

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

作为对比,我们并不能给基本类型动态增加属性,尽管这样不会产生错误:

var name = "zhangsan";
name.age = 28;
alert(name.age); //undefined

复制变量值

对变量进行复制操作时,我们就可以很明显的看到基本类型和引用类型操作的不同了。对基本类型的复制,会创建一个新的值,然后赋值给新的变量:

var num1 = 10;
var num2 = num1;
num1 = 100;
alert(num1); //100
alert(num2); //10

可以看到numnum2是完全独立的两个变量,唯一的关系是num2的初值是和num1相等的,对它们各自的操作不会影响彼此。

var obj1 = new Object();
var obj2 = obj1;
obj1.name = "zhangsan";
alert(obj2.name); //"zhangsan"

这样场景放到引用类型就得到完全不一样的结果,我们发现对obj1的操作(增加name属性)直接影响了obj2obj2也具有了一样的属性和属性值。这就是之前提到的按引用访问的结果,在obj1中保存着的,不是实际的对象本身,而是对象的引用,也就是指向内存中实际对象的一个指针,而当我们赋值给obj2时,其实是把指针的值赋给了它,所以其实他们指向的是一个对象,对象本身并没有被复制,复制的是指针。

传递参数

ECMAScript中所有的参数都是按值传递的,也就是把函数外部的值复制给函数内部的参数,这跟上面的复制变量值是一样的:

function addTen(num){
num += 10;
return num;
}

var count = 10;
var result = addTen(count);
alert(count); //10
alert(result); //20
function setName(obj){
obj.name = "zhangsan";
}

var obj1 = new Object()
setName(obj1);
alert(obj1.name); //"zhangsan"

上面的这个例子很容易就让人产生误解,对于基本类型和引用类型的参数传递结果是完全不一样的,结果看起来像是基本类型是按值传递的,但是引用类型的结果像是引用传递的。但只要仔细想一下就能明白,所谓的引用传递,是去传递参数的地址,这样对形参的修改才会反应到实参上来。这里的结果是对形参的修改确实反应到实参上来了,但这并不是参数传递的原因,而是引用类型保存的差别,是按引用保存的。下面这个例子就验证了引用类型的参数也是按值传递的:

function setName(obj){
obj.name = "zhangsan";
obj = new Object()
obj.name = "lisi";
}

var obj1 = new Object()
setName(obj1);
alert(obj1.name); //"zhangsan"

从例子中就看出,obj1name并不是"lisi"。这就很好的说明了引用类型的参数也是按值传递的。