值类型和引用类型的区别

229 阅读3分钟

值类型与引用类型

typeof 识别出来的类型中,只有 object 和 function 是引用类型,其他都是值类型string、number、boolean、undefined、null、symbol注意:Symbol 也是值类型,实例的唯一标识

javascript对值和引用的赋值/传递在语法上没有区别,完全根据值的类型来决定。

下面来看一个例子:

var a = 2;
var b = a; // ba的值的一个复本
b++;
console.log(a); // 2
console.log(b); // 3
var c = [1,2,3];
var d = c; //d是[1,2,3]的一个引用
d.push(4);
console.log(c); // [1,2,3,4]
console.log(d); // [1,2,3,4]

上例中a、b都是值类型,b更改时,a的值保持不变。

c和d都是引用类型,分别指向同一个复合值[1,2,3]的两个不同引用,指向同一个内存地址。请注意,c和d仅仅是执指向值[1,2,3],并非持有。所以它们更改同一个值(调用.push(4)),它们都指向更改后的新值[1,2,3,4]。

由于引用指向的是值本身而非变量,所以一个引用无法更改另外一个引用的指向。

var a = [1,2,3]
var b = a;
console.log(a); // [1,2,3]
console.log(b); // [1,2,3]

// 然后
b = [4,5,6];
console.log(a); // [1,2,3]
console.log(b); // [4,5,6]

b = [4,5,6]并不影响a指向值[1,2,3],除非b不是指向数组的引用,而是指向a的指针,但在javascript中不存在这种情况。

引用类型经常会在代码中按照下面的写法使用,容易不知不觉中造成错误

var obj = {
    a:1,
    b:[1,2,3]
}
var a = obj.a;
var b = obj.b;
a = 2;
b.push(4);
console.log(obj); // { a:1, b:[1,2,3,4] }
console.log(a,b); // 2, [1, 2, 3, 4]

函数参数指向的值

函数参数经常让人产生这样的困惑:

function foo(x) {
    x.push(4); 
    console.log(x); // [1,2,3,4]
    x = [4,5,6];
    x.push(7)
    console.log(x); // [4,5,6,7]
}
var a = [1,2,3];
foo(a);
console.log(a); // 是[1,2,3,4],不是[4,5,6,7]

我们向函数传递a的时候,实际是将引用a的一个复本赋值给x,而a仍然指向[1,2,3]。在函数中我们通过引用x来更改数组的值(.push(4)之后变成[1,2,3,4])。但x = [4,5,6]并不影响a的指向,所以a仍然指向[1,2,3,4]。

我们不能通过引用x来更改引用a的指向,只能更改a和x共同指向的值。 如果要将a的值变为[4,5,6,7],必须更改x指向的数组,而不是为x赋值一个新的数组。

function foo(x) {
    x.push(4); 
    console.log(x); // [1,2,3,4]
    
    x.length = 0; // 清空数组
    x.push(4,5,67);
    console.log(x); // [4,5,6,7]
}
var a = [1,2,3];
foo(a);
console.log(a); // 是[1,2,3,4],不是[4,5,6,7]

从上例子可以看出, x.length = 0和x.push(4,5,6,7)并没有创建一个新的数组,而是更改了当前的数组。于是a的指向变成了[4,5,6,7]。

如果要将基本类型的值传递到函数内并进行更改,就需要将该值封装到一个复合值(对象、数组等)中,然后通过引用复制的方式传递。

function foo(x) {
    x.a = 42;
}
var obj= {
    a:2
};
foo(obj);
console.log(obj.a); // 42

来源声明:用于记录学习心得整理总结,内容非原创。