引用类型
在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称作是类
,但其实这跟传统的面向对象的语言是不一样的,它并不支持接口等基本功能。对引用类型更确切的称呼是对象定义
,因为它就是在描述一类对象所具有的属性和方法。
Object
类型
前面我们提到变量有5种基本类型(Undefined
, Null
, Boolean
, Number
, String
)和一种复杂类型,这种复杂类型就是Object
类型。Object
类型就是目前为止我们用到的最多的引用类型,也是其实所有引用类型的基础。
引用类型的值有个专有的名词,叫做对象,也是这个引用类型的一个实例。创建一个新的对象使用new
操作符,后跟一个构造函数来创建。构造函数本身就是一个函数,只是这个函数的目的就是用来创建新对象:
var person = new Object(); //person就是一个新的对象,也是Object类型的一个实例 |
基本类型和引用类型的区别
上面提到引用类型有一些专业的术语,像对象,实例等等。除了这些表述的差异,本质上也是有着很大的差异的。
- 对基本类型,是按值访问,直接操作保存在内存中的值;
- 对引用类型,是按引用访问,对对象的操作其实操作的对象的引用。
会在后面的例子中理解这里的区别。
动态属性
基本类型和引用类型的另外的一个很大的区别是,可以进行的操作是不一样的。对引用类型的值,我们可以为其动态的添加、改变和删除其属性和方法:
var person = new Object(); |
作为对比,我们并不能给基本类型动态增加属性,尽管这样不会产生错误:
var name = "zhangsan"; |
复制变量值
对变量进行复制操作时,我们就可以很明显的看到基本类型和引用类型操作的不同了。对基本类型的复制,会创建一个新的值,然后赋值给新的变量:
var num1 = 10; |
可以看到num
和num2
是完全独立的两个变量,唯一的关系是num2
的初值是和num1
相等的,对它们各自的操作不会影响彼此。
var obj1 = new Object(); |
这样场景放到引用类型就得到完全不一样的结果,我们发现对obj1
的操作(增加name
属性)直接影响了obj2
,obj2
也具有了一样的属性和属性值。这就是之前提到的按引用访问的结果,在obj1
中保存着的,不是实际的对象本身,而是对象的引用,也就是指向内存中实际对象的一个指针,而当我们赋值给obj2
时,其实是把指针的值赋给了它,所以其实他们指向的是一个对象,对象本身并没有被复制,复制的是指针。
传递参数
ECMAScript中所有的参数都是按值传递的,也就是把函数外部的值复制给函数内部的参数,这跟上面的复制变量值是一样的:
function addTen(num){ |
function setName(obj){ |
上面的这个例子很容易就让人产生误解,对于基本类型和引用类型的参数传递结果是完全不一样的,结果看起来像是基本类型是按值传递的,但是引用类型的结果像是引用传递的。但只要仔细想一下就能明白,所谓的引用传递,是去传递参数的地址,这样对形参的修改才会反应到实参上来。这里的结果是对形参的修改确实反应到实参上来了,但这并不是参数传递的原因,而是引用类型保存的差别,是按引用保存的。下面这个例子就验证了引用类型的参数也是按值传递的:
function setName(obj){ |
从例子中就看出,obj1
的name
并不是"lisi"
。这就很好的说明了引用类型的参数也是按值传递的。