基本数据类型:按值访问,可以操作保存变量中的实际的值(不同点1不可添加属性和方法不同点2从一个变量到另一个变量复制基本数据类型的值时,会在变量对象上创建一个新的值,然后把该值复制到为新变量分配的位置上(如图1))
引用类型:按引用访问,的值是保存在内存中的对象(js不能访问内存中的位置即不能直接操作对象的内存空间) (不同点1可以添加,改变,删除属性和方法不同点2当一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到新变量分配的空间中。不同的是这个值的副本(也就是绿色的字)其实是一个指针,而这个指针指向存储在堆中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象,因此,改变一个,也会影响另一个(内部值的改变,改变的是内存的对象))图一
图二
函数内参数都是按值传递的(把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样)(参数传递只能按值传递,变量访问可以按值和按引用访问)(向参数传递基本类型值时,被传递的值会被复制给一个局部变量;向参数传递引用数据类型的值时,会把这个值在内存中的地址复制给一个局部变量(也就是新的指针),因此这个值的变化会反应在函数的外部(也就是函数内参数可以改变外部的变量内存属性))
一些名词注释: 首先要弄清楚一些概念:变量、值、值的类型、引用、对象。
如果这些概念没搞清楚,就没办法讨论这个问题。
变量:变量就是一个定义在某个作用域的名字(name),它是一个identifier。它绑定(bind)了一块内存。
值:值就是变量绑定的那块内存里面存放的数据。至于存放的数据的具体代表什么(含义是什么),暂且不管。但是本质上就是一段二进制数。
值的类型:即变量绑定的那块内存里放的数据应该如何去解释,或者说如何解码。在JS中值的类型有很多种,但是可以归结成两类:基本类型和引用类型。基本类型就是说:变量绑定的那块内存里放的数据的含义就是它看起来的那个样子。引用类型就是说:变量绑定的那块内存里放的数据,并不是我真正想要的数据,它是一个元数据、一个地址,一个指向我真正想用的数据的位置的指针。
总结一下就是:变量是一个名字,它绑定一块存储空间,这个空间要么放数据,要么放数据的地址(或者某种引用)。
引用:见上面。
对象:上面说的那些不能被变量直接绑定,而是要通过引用来间接绑定的数据就是对象。而且对象数据与非变量数据相比有一个特点,即它是有内部结构的,即属性。
所以在JS中变量有两种模式:
- 名字->数据
- 名字->数据(引用)->数据(对象)
接下来可以讨论问题了。
问题是讨论JS中变量“访问”有几种类型?按值和按引用?
那么,我们应该首先定义一下什么是“访问”?不知道,因为没有一个地方来给出一个标准定义。但是我猜访问的意思大概是:通过变量去取得你真正想要的数据。假设是这个意思的话,那么很简单,确实是两种,分别对应上面两种变量模式。即基本类型数据就是直接取变量的“值”;而对象类型的数据要通过取变量的值代表的引用,然后再取它指向的对象。
但是,如果不限于“访问”,我们来讨论一下对变量的“操作”呢?
首先来看一下,对一个变量的操作有几种类型。
- 读
- 写
- 属性访问
读:很多操作符都涉及到对变量的读,比如加减乘除等算术运算、关系运算、逻辑运算和位运算等等。这些运算大都是针对基本类型数据(或者先将操作数转为基本类型)的,所以是“按值”操作的。
写:能改变一个变量的值的操作只要三种,即自增、自减和赋值(包括+=这类)。显然也都是“按值”。那么赋值操作呢?也是按值,因为赋值时只是将变量绑定的内存拷贝到另一个变量绑定的内存中,与内存中存的数据的含义无关。
属性访问:属性访问是对对象来说的,只有对象才有属性。(当然JS比较特殊的一点是,对基本类型数据也可以访问属性,这是因为在这里JS执行引擎会把基本类型数据包装成一个对象,这种情况暂不讨论)显然这里需要访问对象的内部状态,要通过引用链接到对象。所以这种情况是按引用。
说了这么多,到后面觉得有些凌乱,有些不严谨了。
试着总结一下:
- 对于对象数据才有的操作,是按引用。比如属性访问。
- 对于基本类型数据才有的操作,是按值。比如加减乘除。
- 对于所有类型数据都有的操作,并且操作的语义与操作数是什么类型无关的情况,按值。比如赋值、传参等。