值类型
ECMAScript 5 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String。
ECMAScript 6中新增加了一种类型 Symbol。
通常你也可以叫它们为原始类型,或者基础数据类型。
引用类型
ECMAScript 中的引用类型有Object 、Array、Function
值类型与引用类型的区别
与所有编程语言一样,值类型存在于stack(栈)中,遵循后进先出的原则,静态分配内存,自动释放内存。引用类型存在于heap(堆)中,堆是内存中的动态区域,当引用类型被创建时会动态分配内存大小,也不会自动释放内存,除非手动设置值为null,故创建对象不应过于泛滥,否则会产生内存问题。
值类型的访问是按值访问,操作的是变量中的实际的值。
引用类型的访问是按其在堆中的引用地址访问,根据其在堆中的地址找到值。
值传递与引用传递(传值与传址)
var stackVar = "test";
var heapVar = [0, 1, 2, 3, 4]
var heapVar2 = heapVar;//引用类型间的相互赋值操作只是引用了同一个内存地址
var heapVar3 = heapVar;
heapVar3 =[];//此处赋值了一个新的对象[],相当于变量heapVar3重新指向了一个内存空间
console.log(heapVar);//0, 1, 2, 3, 4
heapVar2.unshift(-1);
function test1(str) {
str = "stackVar"
}
function test2(arr) {
arr = [];
}
function test3(arr) {
arr.push(5);
}
console.log(stackVar);//test
test1();
console.log(stackVar);//test
console.log(heapVar);//-1,0,1,2,3,4
test2(heapVar);
console.log(heapVar);//-1,0,1,2,3,4
test3(heapVar);
console.log(heapVar);//-1,0,1,2,3,4,5
引用类型数据的浅拷贝和深拷贝
前面已经说到了,引用类型在传递过程中只是传递的一个地址,传递到函数内进行相关操作会影响外部值。
所以,当我们想避免这种情况时,需要对数据进行复制,有2种情况,如下。
浅拷贝
var a = {
k1: "1",
k2:[1,2]
}
function Copy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
return c;
}
var b = Copy(a);
b.k1 = "2";
b.k2.push(3,4)
console.log(a.k1); //"1"
console.log(a.k2);//[1,2,3,4]
上例的变量b拷贝了a,但是改变了b的值后,a.k1未受影响,a.k2却受影响,这是因为a.k2是引用类型,
即父子对象存在地址共享,为了解决此问题,正确的复制操作应该为深拷贝。如下
深拷贝
var a = {
k1: "1",
k2: [1, 2]
}
function Copy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
Copy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
var b = {};
b = Copy(a, b);
b.k1 = "2";
b.k2.push(3, 4)
console.log(a.k1); //"2"
console.log(a.k2);//[1,2]
上例通过递归找到数据中所有的引用类型数据,通过赋值操作将其指向一个新的地址,从而切除浅拷贝中的对象引用地址共享的问题。