JS数据类型与深浅拷贝

98 阅读4分钟

数据类型

基本数据类型

  • number
  • string
  • boolean
  • undefined
  • null
  • Symbol

引用数据类型

  • 对象
  • 对象的子类型,包括函数,数组,内置对象如StringNumber

确定类型

typeof

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"

function f() {}
typeof f// "function"

a
typeof a// "undefined" //检查没有声明的变量

typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"

可以看出使用 typeof 不能区分数组和对象的类型,这时可以使用instanceof

instanceof

var o = {};
var a = [];

o instanceof Array // false 
a instanceof Array // true

Object.prototype.toString

Object.prototype.toString.call(123);    //[object Number]
Object.prototype.toString.call('123');    //[object String]
Object.prototype.toString.call(undefined);    //[object Undefined]
Object.prototype.toString.call(true);    //[object Boolean]
Object.prototype.toString.call({});    //[object Object]
Object.prototype.toString.call([]);    //[object Array]
Object.prototype.toString.call(function(){});    //[object Function]
Object.prototype.toString.call(null);    //[[object Null]]

注意点

undefined 与 null

  1. 出现undefined的情况
function f(x) {
  return x;
}
f() // 调用函数时,应该提供的参数没有提供 返回undefined

var  o = new Object();
o.p //对象没有赋值的属性 undefined

function f() {}
f() //函数没有返回值时 undefined
  1. undefinednull区别
  • null是一个表示“空”的对象,转为数值时为0;undefined是一个表示"此处无定义"的原始值,转为数值时为NaN;
  • undefinednull与任何有意义的值比较返回的都是false,但是nullundefined之间不严格比较返回的是true

布尔值为false的情况

这六个值都被转为false,其他值都视为true

  • undefined
  • null
  • false
  • +0、-0
  • NaN
  • " "或' '

NaN

注意NaN是一个特殊数值,而不是一种数据类型。 可认为NaN是一个“警戒值”,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”

深拷贝与浅拷贝

区别

首先要明确基本数据类型和引用数据类型的存储位置

  • 基本数据类型:存放在栈区
  • 引用数据类型:数据在堆区中,栈区存放着指向堆内存中该数据的指针,也就是说,需要通过栈区的指针区访问到数据,而不能直接访问到堆中的数据

深浅拷贝都是对于引用数据类型而言的。 浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。

let person1 = {
  name:'Bob',
  more:{
    age:23
  }
}

let person2 = person1 //浅拷贝
console.log(person2.more.age) //23
person1.more.age = 12
console.log(person2.more.age) //12

只有深拷贝才是真正地对对象的拷贝。 深拷贝不像浅拷贝那样只是复制了一层引用,就连值也都复制了,两个对象不会互相影响。

let person1 = {
  name:'Bob',
  more:{
    age:23
  }
}

let person2 = deepClone(person1) //深拷贝
console.log(person2.more.age) //23
person1.more.age = 12
console.log(person2.more.age) //23

实现

递归

接下来看看如何实现深拷贝吧
明确了引用数据类型有哪些,判断后进行递归便可得到深拷贝后的结果

function deepClone(source){
    const target = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
    for(let keys in source){ 
      if(source.hasOwnProperty(keys)){ 
        if(source[keys] && typeof source[keys] === 'object'){ 
          target[keys] = source[keys].constructor === Array ? [] : {};
          target[keys] = deepClone(source[keys]);
        }else{ // 如果不是,直接赋值
          target[keys] = source[keys];
        }
      } 
    }
    return target;
  }

JSON的解析与序列化

json.stringify将 js 对象序列化为 json 字符串
json.parse将 JSON 字符串解析为原生 js 值

let person1 = {
  name:'Bob',
  more:{
    age:23
  }
}
let jsonText = JSON.stringify(person1) //'{"name":"Bob","more":{"age":23}}'
let personCopy = JSON.parse(jsonText) 
//{name:'Bob',more:{age:23}}

但是这种方法会忽略所有函数及原型成员,值为undefined的任何属性也会被跳过,结果中最终都是值为有效 JSON 数据类型的实例属性。

JS中数组和对象自带的拷贝方法

数组

  • concat

来自MDN
将被合并到一个新的数组中。如果省略了所有参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝 注意:这里说的浅拷贝并不是 ‘=’,而是进行了首层的深拷贝


const sourceArr = [1,2,3,4]
const targetArr = sourceArr.concat()
console.log(sourceArr === targetArr)//false
sourceArr.push(5)
console.log(targetArr)//[1,2,3,4]


const sourceArr = [1,2,3,4,[5,6]]
const targetArr = sourceArr.concat()
console.log(sourceArr === targetArr)//false
sourceArr[4].push(7)
console.log(targetArr)[1,2,3,4,[5,6,7]]
  • slice
同concat只是进行了首层拷贝
  • 扩展运算符
let sourceArr = [1,2,3,4,[5,6]];
let targetArr = [...sourceArr];
targetArr[0] = 0;
console.log(sourceArr); // [1,2,3,4,[5,6]]
targetArr[4].push(7);
console.log(sourceArr); // [1,2,3,4,[5,6,7]]

对象

  • Object.assign()

来自MDN
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。原对象也会更改。

const sourceObj = {a:1,b:{c:2}};
const targetObj = Object.assign({}, sourceObj);
targetObj.a = 2;
targetObj.b.c = 3;
console.log(sourceObj); // {a:1,b:{c:3}}

同样只是进行了首层拷贝

  • 扩展运算符
const sourceObj = {a:1,b:{c:2}};
const targetObj = {...sourceObj};
targetObj.a = 2;
targetObj.b.c = 3;
console.log(sourceObj); // {a:1,b:{c:3}}

同样只是进行了首层拷贝

总结

搞清楚js的数据类型后再进行深拷贝,递归才是真正实现深拷贝的方式。