小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
在工作中,很经常有同事叫我帮忙看一下,为什么这个数据,我都没去改变它,它的值却变了。其实这个问题的原因很简单,就是这个数据是个引用类型而已。
什么是引用类型
在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
不影响。