小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
在工作中,很经常有同事叫我帮忙看一下,为什么这个数据,我都没去改变它,它的值却变了。其实这个问题的原因很简单,就是这个数据是个引用类型而已。
什么是引用类型
在JavaScript中的数据有两种类型,一种是基本类型,一种是引用类型。基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的。
我们把一个引用类型的数据Object赋值给变量obj1时,变量obj1存放在栈内存中,数据Object存放在堆内存中。我们通过访问变量obj1时,栈内存中的变量obj1会去引用(访问)堆内存中的数据Object,故将数据Object称为引用类型的数据。
如果把变量obj1在赋值给变量obj2,变量obj2会添加到栈内存中,当我们访问变量obj2时,栈内存中的变量obj2还是会去访问堆内存中的数据Object。
如果给变量obj2添加了name属性,会改变堆内存中的数据Object,此时再访问变量obj1就会发现多了个name。
在实际业务中,如果不小心把一个被引用类型的数据赋值的变量,再赋值给另外一个变量,改变这个变量,就会产生本文开头所描述的现象--谁动了我的数据。
原生的引用类型
在JavaScript中原生的引用类型有这几种:Object、Array、RegExp、Date、Function、特殊的基本包装类型(String、Number、Boolean)以及单体内置对象(Global、Math)。而最常用的只有对象(Object)、数组(Array)两种。
如何避免数据被莫名地改变
避免数据被莫名地改变,很简单,遇到引用类型数据赋值给变量前,先把数据进行处理,返回一个新数据后在赋值。处理的目的就是返回一个新数据,有以下几种方法:
-
克隆
对对象或数组进行克隆都能返回一个新数据。
-
JSON.stringify()和JSON.parse()先用
JSON.stringify()把对象或数组转成一个JSON字符串,在用JSON.parse()将其解析,会返回一个新的对象或数组。 -
slice
const arr = [1,2]; const arr1 = arr.slice();以上
arr1是一个新数组,改变对arr不影响。 -
map
const arr = [1,2]; const arr1 = arr.map((item) =>{ return item; })以上
arr1是一个新数组,改变对arr不影响。 -
filter
const arr = [1,2]; const arr1 = arr.map((item) =>{ return true; })以上
arr1是一个新数组,改变对arr不影响。 -
concat
const arr = [1,2]; const arr1 = arr.concat()以上
arr1是一个新数组,改变对arr不影响。 -
Object.assign
const obj = { a:1 }; const obj1 = Object.assign(obj);以上
obj1是一个新对象,改变对obj不影响。 -
ES6扩展运算符
const arr = [1,2]; const arr1 = [...arr]以上
arr1是一个新数组,改变对arr不影响。const obj = { a:1 }; const obj1 = {...obj};以上
obj1是一个新对象,改变对obj不影响。