谁动了我的数据 | 程序员必备小知识

3,545 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

在工作中,很经常有同事叫我帮忙看一下,为什么这个数据,我都没去改变它,它的值却变了。其实这个问题的原因很简单,就是这个数据是个引用类型而已。

什么是引用类型

在JavaScript中的数据有两种类型,一种是基本类型,一种是引用类型。基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的。

我们把一个引用类型的数据Object赋值给变量obj1时,变量obj1存放在栈内存中,数据Object存放在堆内存中。我们通过访问变量obj1时,栈内存中的变量obj1会去引用(访问)堆内存中的数据Object,故将数据Object称为引用类型的数据。

如果把变量obj1在赋值给变量obj2,变量obj2会添加到栈内存中,当我们访问变量obj2时,栈内存中的变量obj2还是会去访问堆内存中的数据Object

image.png

如果给变量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不影响。