最近一直在纠结这个问题,决定彻底搞懂深浅拷贝,为了方便理解和记忆,决定将python和js放在一起探讨下。
首先看下直接用'='赋值两种语言的表现。
JavaScript:
var l1 = [['js','python'],12,{"name":"jack"}]
var l2 = l1
l1.push('emmmmm')
l1[0].push('go')
console.log('l1',l1)
console.log('l2',l2)
console.log('l1 === l2:',l1 === l2)
/*
输出:
l1 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' }, 'emmmmm' ]
l2 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' }, 'emmmmm' ]
l1 === l2: true
*/
Python:
l1 = [['js','python'],12,{"name":"jack"},(13,14)]
l2 = l1
l1.append('emmmmm')
l1[0].append('go')
print('l1',l1)
print('l2',l2)
print('l1 is l2',l1 is l2)
'''
输出:
l1 [['js', 'python', 'go'], 12, {'name': 'jack'}, (13, 14), 'emmmmm']
l2 [['js', 'python', 'go'], 12, {'name': 'jack'}, (13, 14), 'emmmmm']
l1 is l2 True
'''
毫无疑问:用等号赋值就是直接指向拷贝对象,两者完全相同
浅拷贝
用构造器实现浅拷贝
JavaScript实现浅拷贝的几种方式:
- 数组浅拷贝array.slice()
var l1 = [['js','python'],12,{"name":"jack"}]
var l2 = l1.slice()
l1.push('emmmmm')
l1[0].push('go')
console.log('l1',l1)
console.log('l2',l2)
console.log('l1 === l2:',l1 === l2)
/*
l1 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' }, 'emmmmm' ]
l2 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' } ]
l1 === l2: false
*/
-
Object.assign()——ES6语法
var l1 = [['js','python'],12,{"name":"jack"}] var l2 = Object.assign(l1) l1.push('emmmmm') l1[0].push('go') console.log('l1',l1) console.log('l2',l2) console.log('l1 === l2:',l1 === l2) /* 输出: l1 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' }, 'emmmmm' ] l2 [ [ 'js', 'python', 'go' ], 12, { name: 'jack' }, 'emmmmm' ] l1 === l2: true */
-
扩展符...
var l1 = [['js','python'],12,{"name":"jack"}] console.log(...l1) l1.push('emmmmm') l1[0].push('go') console.log(...l1) /* 输出: [ 'js', 'python' ] 12 { name: 'jack' } [ 'js', 'python', 'go' ] 12 { name: 'jack' } 'emmmmm' */
Python:
l1 = [['js','python'],12,{"name":"jack"},(13,14)]
l2 = list(l1)
l1.append('emmmmm')
l1[0].append('go')
l1[3] += (15,16)
print('l1',l1)
print('l2',l2)
print('l1 is l2',l1 is l2)
'''
输出:
l1 [['js', 'python', 'go'], 12, {'name': 'jack'}, (13, 14, 15, 16), 'emmmmm']
l2 [['js', 'python', 'go'], 12, {'name': 'jack'}, (13, 14)]
l1 is l2 False
'''
可以看出,python、JavaScript的拷贝都是浅拷贝:即子元素都是拷贝对象子元素的引用。
比较特殊的是python存在元组这种类型,元组是不可变的,因此l1对元组的操作实际上是创建了一个新元组,而l2并没有重新指向新元组。
深拷贝
深度拷贝,是指重新分配一块内存,创建一个新的对象,并且将原对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中。因此,新对象和原对象没有任何关联。
Python:
import copy
l1 = [['js','python'],12,{"name":"jack"},(13,14)]
l2 = copy.deepcopy(l1)
l1.append('emmmmm')
l1[0].append('go')
l1[3] += (15,16)
print('l1',l1)
print('l2',l2)
print('l1 is l2',l1 is l2)
'''
输出:
l1 [['js', 'python', 'go'], 12, {'name': 'jack'}, (13, 14, 15, 16), 'emmmmm']
l2 [['js', 'python'], 12, {'name': 'jack'}, (13, 14)]
l1 is l2 False
'''
JavaScript实现深拷贝的几种方式:
-
JSON.parse/stringify
这种方法适用于:对象中不包含Date函数,undefined,Infinity,正则,映射,集合,Blob,FileLists,ImageDatas,稀疏数组,类型化数组或其他复杂类型。如果包含了上述类型,深拷贝会出现偏差
const a = { string: 'string', number: 123, bool: false, nul: null, date: new Date(), // stringified undef: undefined, // lost inf: Infinity, // forced to 'null' re: /.*/, // lost } console.log(a); console.log(typeof a.date); // Date object const clone = JSON.parse(JSON.stringify(a)); console.log(clone); console.log(typeof clone.date); // result of .toISOString() /* 输出: { string: 'string', number: 123, bool: false, nul: null, date: 2019-09-02T03:25:11.116Z, undef: undefined, inf: Infinity, re: /.*/ } object { string: 'string', number: 123, bool: false, nul: null, date: '2019-09-02T03:25:11.116Z', inf: null, re: {} } string */
-
使用一些库的深拷贝函数
-
loadsh.cloneDeep()
const loadsh = require('loadsh') const a = { string: 'string', number: 123, bool: false, nul: null, date: new Date(), // stringified undef: undefined, // lost inf: Infinity, // forced to 'null' re: /.*/, // lost } const clone = loadsh.cloneDeep(a) console.log(a) console.log(clone) /* 输出: { string: 'string', number: 123, bool: false, nul: null, date: 2019-09-02T07:21:57.823Z, undef: undefined, inf: Infinity, re: /.*/ } { string: 'string', number: 123, bool: false, nul: null, date: 2019-09-02T07:21:57.823Z, undef: undefined, inf: Infinity, re: /.*/ } */
-
angular.copy()
// Module: copyExample angular. module('copyExample', []). controller('ExampleController', ['$scope', function($scope) { $scope.leader = {}; $scope.reset = function() { // Example with 1 argument $scope.user = angular.copy($scope.leader); }; $scope.update = function(user) { // Example with 2 arguments angular.copy(user, $scope.leader); }; $scope.reset(); }]);
-