js对象引用

简介:在js中,做一些赋值操作的时候,有些是简单的赋值,有些是对象的引用,对象的引用的时候会出现一些莫名的问题,老是搞不清楚,所以特意总结下,哪些情况下回出现对象引用而如何去避免和利用这些问题。

js中的原始值(undefined、null、boolean、number、string)与引用值类型(对象、数组、函数等)有着根本的区别

存储方式

原始值存储在栈(stack)中的简单数据段,它们的值直接存储在变量访问的位置。

引用类型是存储在堆(heap)中的对象,存储在变量处的值是一个指针(point),指向存储对象的内存地址。

如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查询的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址是大小固定的,所以把它存储在栈中对变量性能无任何负面影响。
js对象存储分配

表现方式

原始值是不可更改的,任何方式都无法更改一个原始值,只能给它重新赋值覆盖掉原来的值。对数字和布尔值来说显然如此,但是对字符串来说就不那么明显了,因为字符串看起来像由字符组成的数组。我们期望可以通过指定索引来修改字符串中的字符。实际上,js是禁止这样做的。
创建字符串并修改第三个,发现字符串还是没变:

1
2
3
var str = 'abcdefg';
str[3] = '0';
console.log(str); // abcdefg

字符串中所有内置方法看上去是返回了一个修改后的字符串,实际上返回的是一个新的字符串值。

1
2
str.toUpperCase(); // ABCDEFG
console.log(str); // abcdefg

原始值的比较是值的比较,只有它们的值相等时它们才相等。对字符串来讲,当且仅当它们的长度相等且每个索引的字符都相等时,它们才是相等的。

对象值都是引用,对象的比较是基于引用的比较,当且仅当它们引用同一个基对象时,它们才相等。并且,修改引用对象时,基对象也会被修改。

1
2
3
4
5
6
var a = [];
var b = [];
var c = a;
console.log(a == b); // false
console.log(a == c); // true
a.push(1) // a: [1], b: [], c: [1]

总结:普通值就是普通值,存储对象的变量可以看做指向内存的指针。复制对象变量的时候其实是复制指针,新变量也是指向这个地址的对象,他们共享着这个对象的内存。所以其中一个进行修改这个对象,对象变了,另一个也会显示变化。

按共享传递(call by sharing)

准确的说,js中的基本类型按值传递,对象类型按共享传递。

按共享传递策略的重点是:调用函数传参时,函数接受对象实参引用的副本。它和按引用传递的不同在于:在共享传递中对函数形参的复制,不会影响实参的值。也就是说形参只是对象变量的一个指针的副本,两个指向同一个对象,可以修改对象,但是赋值只会覆盖当前变量本身,也就是覆盖的是指针副本,对对象没有影响。

下面将数组a传递给方法,然后在方法里push一个值,发现实参也push了。

1
2
3
4
5
var a = [1,2,3];
function add(arr){
arr.push(5);
}
add(a); // a:[1,2,3,5]

下面在方法中将arr赋值,实参没有改变。

1
2
3
4
5
var a = [1,2,3];
function add(arr){
arr = 1;
}
add(a); // a:[1,2,3]

有钱的捧个钱场,没钱的捧个人场~