一、问题起源
var a = 0;
var x = a;
a = 1;
console.log(a); //1
console.log(x); //0
第一段代码中,a与x互不影响;
var b = {n:0};
var y = b;
b = {n:0};
console.log(b); //{n:0}
console.log(y); //{n:0},
第二段代码,只更改了b,y却也跟着变了。
为什么呢?因为在x = a的赋值是值传递,而在y = b的赋值是引用传递。
二、堆栈结构、原始值和引用值
在JavaScript中,每声明一个变量,CPU便会在栈中申请一个区域,用以存放变量的值。那么在赋值给变量时会出现以下两种情况:
-
将简单的数据段赋值给变量时,可将他们直接存储在较小的内存区域 - 栈中;
此时,存放在变量中的值是数据段本身,即原始值 -
对于较复杂的数据段(譬如对象),它们的占据大小无法确定,时而十分庞大,直接存于栈中会占用大量空间。此时,可以将复杂数据段存放在较大区域 - 堆中,并生成一个指向该数据段的首地址值存于栈中。
此时,存放在变量中的值是数据段的首地址值,即引用值
也就是说,在JavaScript中,变量可以存储着两种类型的值,原始值和引用值。
var num = 0; //变量num中存的是原始值,也就是number数据0
var obj = {n:0}; //变量obj中存的是引用值,也就是object数据{n:1}的首地址值
三、数据类型
了解值传递和引用传递之前,首先要理解JavaScript的数据类型。
ES6 标准定义了 7 种数据类型:
6种原始类型(Primitive type)
- boolean
- null
- undefined
- number
- string
- symbol ( new in ES6, 暂不考虑 )
和对象object
注意:var str = new String(); 中str = {" ", length}是object
四、值传递和引用传递
1. 原始值和值传递
原始值即数据本身,在JavaScript的原始类型数据boolean、undefined、null、number、string均为简单数据段。当将这些数据赋值给变量时,是直接将数据本身存储在变量容器中,这便是值传递。
var a = true;
var b = undefined;
var c = "ccc"
var x = a, y = b, z = c;
x = 99;
console.log(a); //true
console.log(b); //undefined
console.log(c); //ccc
console.log(x); //99
console.log(y); //undefined
console.log(z); //ccc
在变量abcxyz中,存储的是数据本身。在执行x = a赋值时,直接将a中的原始值复制给了x,所以在对变量x进行操作时,不会影响变量a。
2. 引用值和引用传递
引用值即存储于堆中的对象的首地址值。将对象 {n:0} 赋值给变量x时,会在堆中申请一块空间存储对象数据 {n:0} ,并将其首地址值放在变量x中,这便是引用传递。
var x = {n:0};
var y = x;
var z = {n:9};
console.log(x); //{n:0}
y.n = 1;
console.log(x); //{n:1}
console.log(z); //{n:9}
在变量xyz中,存储的是引用值,在y = x赋值时,是将x中存储的引用值复制给y,它们指向同一个对象。所以操作y时,实际上是在操作y所指向的对象。这就导致了,虽然代码中只操作了y,但x也随之改变,而变量z所指向的对象不同,它并没有被影响。