最近同事在使用js的过程中,遇到了一个比较奇葩的问题,使用场景是在js客户端接收到后端传过来的数据之后,他修改了这个数据内容而导致bug无法定位是前端还是后端的问题。

在框架底层,当框架接收到后端消息之后打印了一次日志,日志内容是后端传过来的数据内容。

当数据体传到上层客户时,同事将这个数据体的一部分存储到了临时变量中,并且修改了这个变量中的一个值。

由于某些原因,导致了一个bug,为了解决这个bug需要对数据的值进行排查,结果在分析日志的时候,发现打印的值是有问题的,所以前端同事以为是后端传过来的值有问题,于是,后端同事进行了排查,发现后端传过来的值跟前端打印的值不一样。

导致这个问题的原因其实就跟传参的类型有关系。

我们知道,js中的数据的类型有两大类:

  1. 字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
  2. 对象(Object)、数组(Array)、函数(Function)。

[cr_alert style=”yellow”]注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。[/cr_alert]

这两种类型就导致了参数在传递过程中的不同表现:

  1. 参数通过值传递
    • 这种情况下,如果函数改变了参数的值,它不会改变参数的原始值。
  2. 参数通过引用传递
    • 这种情况下,如果函数改变了参数的值,原始值也会改变。
//值传递
var num = 1;
function xx(arg){
   arg=2;
}
xx(num);
console.log(num);//1
//对象
var a = {b:1};
function xx(obj){
    obj.b=2;
}
xx(a);
console.log(a);//{b: 2}

//数组
var arr = [1,2];
function xx(obj){
    obj[0]=2;
}
xx(arr);
console.log(arr);// [2, 2]

需要注意的是,引用传递只有在改变了函数参数(argument)传递的对象、数组、函数内容,如属性、元素值才会导致原始值被改变,如果直接改变函数参数(parameter)的引用是不会导致原始值改变的:


//这里注意观察与上面对象代码块的不同
var a = {b:1};
function xx(obj){
    obj = {x:2};
}
xx(a);
console.log(a);//{b: 1}

经过对比观察,我们向函数传递a的时候,实际是将引用a的一个副本赋值给obj,而a仍然指向{b:1}。在函数中,我们可以通过更改引用obj来更改对象的值,使用obj.b=2之后a的值变为{b: 2}。但obj = {x:2}并不影响a的指向,所以a仍然指向{b:1}。这也是决定了原始值是否被改变的关键因素,在使用过程中需要特别小心谨慎。

在使用过程中,如果你不想自己的原始值被改变,方法还是很多的,后面我会专门出一篇关于如何防止对象值被改变的文章,现在你自己亲自来试一试吧!