一道题目引发的思考

  let spaceship = {
    homePlanet : 'Earth',
    color : 'red'
  };
  let tryReassignment = obj => {
    obj = {
    identified : false, 
    'transport type' : 'flying'
    }
    console.log(obj) // {'identified': false, 'transport type': 'flying'}
  };
  tryReassignment(spaceship)
  console.log(spaceship) // {homePlanet: "Earth", color: "red"}

为什么在函数内改变参数的值,看上去改变成功,但其实并没有改变成功。
那么从头开始讲这是为什么?

JavaScript有5种基本的数据类型,分别是:布尔、null、undefined、String和Number,还有引用类型值,分别是 Array、Function和Object。
这俩种数据类型在值绑定的过程区别是很大的,像基本数据类型,在变量赋值过程中就是讲这个值绑定到这个变量上,但是引用类型值则不同,它是讲这个值所对应的内存地址绑定给这个变量。这种不同的赋值方式分别叫值传递和引用传递。
下面用表格的方式来描述下

var obj = {}; // 步骤一
obj.val = 'value'; // 步骤二
var newObj = obj; // 步骤三

// 步骤一时

变量 地址
obj #001 {}

// 步骤二时

变量 地址
obj #001 {val: 'value'}

// 步骤三时

变量 地址
obj #001 {val: 'value'}
newObj #001

可以看出obj的地址时一直没变的,当obj被赋值给另一个变量时它只是将地址传递给另一个变量,这就是引用传递。
明白了引用传递和值传递,那么你就会这两个和上面的题目有上面关系呢?下面就开始讲讲两者的关系。
JS函数的参数与大多数其他语言中函数的参数有所不同,JS函数不介意传递进来多少个参数,也不在乎传递进来的参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数也未必一定要传递两个参数。可以传递一个,三个甚至不传递参数,而解析器永远也不会有什么怨言。原因是JavaScript中的参数在内部是用一个数组来表示的。**函数接受到始终是这个数组,而不关心数组包含多少元素和参数。**实际上在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。
js是数组类型来接受参数,那么下面就画个上面题目的示意图:

变量 地址
spaceship #001 {homePlanet: "Earth", color: "red"}
obj(参数) #001

在函数内部,obj进行一次变量赋值,obj是对象类型,因为是引用传递,所以将obj的地址换了一个新的地址

变量 地址
obj(参数) #002 {'identified': false, 'transport type': 'flying'}

但是由于是地址更换了,并未对之前地址的值对修改,所以未改变传的值。
当然有人觉得传值也是可以理解的,因为值被赋值到了另一个对象里而已,那么怎么证明是传的是地址呢?下面再出一题改的是地址的值

var a = {
    key: 'name',
    value: 2121
}
var handleValue = (obj) => {
    obj.key = 'otherKey';
    console.log(obj); // {key: "otherKey", value: 2121}
}
handleValue(a);
console.log(a); // {key: "otherKey", value: 2121}

这题中handleValue函数参数传的是{key: "name", value: 2121}的地址,在函数内部将改地址对应的值修改了,所以最开始定义的变量也被修改了
那么我们也可以开始实验,将参数的地址换一个而不是之前的a所对应的地址,那么怎么修改呢?当然是用深拷贝啊。

function deepCopy (source) {
  const result = source.constructor === Array ? [] : {}; // 用三目运算判断他是数组还是对象
  for (const key in source) {
    if (typeof source[key] === 'object') {
      result[key] = deepCopy(source[key]);
    } else {
      result[key] = source[key];
    }
  }
  return result;
};
var a = {
    key: 'name',
    value: 2121
}
var handleValue = (obj) => {
    console.log(obj); // {key: "name", value: 2121}
    obj.key = 'otherKey';
    console.log(obj); // {key: "otherKey", value: 2121}
}
handleValue(deepCopy(a));
console.log(a); // {key: "name", value: 2121}

a并没有修改,但是在函数内部的参数obj已经被修改,正是说明参数obj所对应的地址已被修改,只是这次obj对应的地址并不是和之前的a所对应的地址一样了。