js的对象拷贝总结
总结一下 JavaScript 中对象拷贝的问题。
先说 JavaScript 中的数据类型
JavaScript 是一种弱类型的编程语言,所谓弱类型就是在变量定义的时候不需要指定其变量的类型,变量的类型会在变量赋值时自动做判断。
JavaScript 中的数据类型分为两大类:值类型(又叫:原始数据类型)以及引用类型。
值类型
所谓值类型,简单的说,就是申请的变量(栈)内存中直接存了该变量的值,值类型包括以下几种:undefined、null、boolean、number、string、symbol。
1 | // 值类型 |
引用类型
所谓引用类型,就是说,变量中存的并不是变量本身,而是一个指向某个(堆)内存的地址,该堆内存中放的是变量引用的内容。显而易见,程序中可以定义多个变量来引用该内容,他们指向的是同一个内容,如果其中一个修改了引用的内容,其他变量也会受到影响(这可能会导致一些异常)。
JavaScript 中的引用类型为 Object 类,包含 Function, Array, Date 等内置对象。
1 | // 引用类型 |
1 | var obj = { |
对象拷贝(克隆/复制)
对象的拷贝分可以简单的分为浅拷贝和深拷贝。简单的说,浅拷贝只拷贝对象的引用,对象在内存中还只是那一个;而深拷贝则拷贝了对象的实例,在内存中开辟了一个新的空间。
从上面介绍的 JavaScript 数据类型中可以知道很清楚的看到,对一个对象而言,浅拷贝只要遍历一遍对象第一层的属性,然后赋值给一个新的对象即可,或者再简单一点,直接将源对象赋值给目标对象。而,要实现深拷贝,就要将对象进行递归遍历,直到最后一层的属性值是原始数据类型。
浅拷贝实现
1 | function shallowClone(source){ |
深拷贝实现
深拷贝的实现,只要在浅拷贝的基础上加上递归判断,如果对象的属性值仍然是个对象,那就递归调用拷贝方法,知道对象的属性是一个原始数据类型。
1 | function deepClone(source){ |
对象序列化实现深拷贝
1 | var target = JSON.parse(JSON.stringify(source)); |
对象序列化之后得到一个字符串,而字符串本身是原始数据类型,传值的,所以,再将字符串 parse 成一个新的对象就可以实现对象的深拷贝。需要注意的是,这种方法虽然简单,但是需要保证源对象(source)是 JSON 安全的,只适用于部分情况。如果,对象中包含方法,则方法无法完成序列化,就无法将方法深拷贝。
一些题外话
判断数据类型的方法
typeof
typeof 的值有以下几种:undefined boolean number string object function、symbol。需要注意的是:
- typeof null === “object”, 但是 null 是原始数据类型,并非对象。
- typeof [1,2] === “object”, 引用类型中,除了 function 之外,其他的 typeof 都是 object。 typeof function(){} === “function”。
- typeof Symbol() === “symbol”, ES6 新增。
instanceof
实例和构造函数之间的对应。例如判断一个变量是否是数组,使用typeof无法判断,但可以使用[1, 2] instanceof Array来判断。因为,[1, 2]是数组,它的构造函数就是Array。
toString
var type = Object.prototype.toString.call(obj);
obj类型 | type的值 |
---|---|
Array | “[object Array]” |
Function | “[object Function]” |
RegExp | “[object RegExp]” |
Date | “[object Date]” |
Number | “[object Number]” |
String | “[object String]” |
Boolean | “[object Boolean]” |
null | “[object Null]” |
undefined | “[object Undefined]” |
Object/对象字面量 | “[object Object]” |