数据类型
- 基本数据类型:简单的数据,保存基本数据类型的变量是按值进行访问的,因此直接保存在栈内存中
- 引用数据类型:保存在堆内存中,在栈内存中保存了一个堆内存中实际对象的引用,引用数据类型的变量是按照引用进行访问的
复制值
- 基本数据类型:在通过变量把一个基本数据赋值到另一个变量时,基本数据会被复制到新变量的位置
1 2
| let num1 = 5; let num2 = num1;
|
从上图可知,num1和num2是独立使用的,互不干扰
- 引用数据类型:在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象,操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来
深浅拷贝
深浅拷贝都是针对于引用类型的,因为引用类型是存放在堆内存中的,在栈地址中有一个或者多个地址来指向堆内存的某一数据
浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象
浅拷贝是指被复制对象的所有变量都与原来的对象含有相同的值,而所有的对其他对象的引用仍然指向原来的对象
浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,如果你修改了“副本”的值,那么原来的对象也会被修改
浅拷贝只拷贝一层,深层次的引用类型则共享内存地址
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
深拷贝是一个整个独立对象的拷贝,深拷贝会拷贝所有的属性,并且拷贝属性指向的动态分配的内存
深拷贝把要复制的对象所引用的对象都复制了一遍,如果你修改了“副本”的值,那么原来的对象不会被修改,两者是相互独立的
实现浅拷贝的方式
Object.assign
Array.protptype.slice
Array.protptype.concat
- 使用拓展运算符
- 手写浅拷贝
Object.assign
补课:Object.assign
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, };
let person1 = Object.assign({}, person);
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
|
Array.prototype.slice()
补课:slice
1 2 3 4 5 6 7
| const colors = ['red', 'yellow', ['green', 'black']]; const colors1 = colors.slice(0);
colors1[2][1] = 'pink'; console.log(colors); console.log(colors1);
|
Array.prototype.concat()
补课:concat
1 2 3 4 5 6 7
| const colors = ['red', 'yellow', ['green', 'black']]; const colors1 = colors.concat();
colors1[2][1] = 'pink'; console.log(colors); console.log(colors1);
|
拓展运算符
1 2 3 4 5 6 7
| const colors = ['red', 'yellow', ['green', 'black']]; const colors1 = [...colors];
colors1[2][1] = 'pink'; console.log(colors); console.log(colors1);
|
手写浅拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| function shallowClone(target) { let res, targetType = Object.prototype.toString.call(target).slice(8,-1).toLowerCase(); if (target === 'object') { res = {}; } else if (target === 'array') { res = []; } else { return target; } for (let key in target) { res[key] = target[key]; }; return res; };
const colors = ['red', 'yellow', ['green', 'black']]; const colors1 = shallowClone(colors);
colors1[2][1] = 'pink'; console.log(colors); console.log(colors1);
let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, };
let person1 = shallowClone(person);
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
|
实现深拷贝的方式
_.cloneDeep()
JQuery.extend()
JSON.stringify()
- 手写深拷贝
_.cloneDeep()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const _ = require('loash'); let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, }; let person1 = _.cloneDeep(person);
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
|
JQuery.extend()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const $ = require('jquery'); let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, }; let person1 = $.extend(person);
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
|
JSON.stringify()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, };
let person1 = JSON.parse(JSON.stringify(person));
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
console.log(person1); console.log(person);
|
这种方法会存在弊端:会忽略undefined
,function
,symbol
的存在
1 2 3 4 5 6 7 8 9 10 11 12
| const test = { a: 'hello', b: undefined, c: Symbol('I am fine'), d: function() { console.log('and you?'); }, };
const test1 = JSON.parse(JSON.stringify(test));
console.log(test1);
|
手写深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| function deepClone(target) { let res, targetType = Object.prototype.toString.call(target).slice(8, -1).toLowerCase(); if (targetType === 'object') { res = {}; } else if (targetType === 'array') { res = []; } else { return target; }; for (let key in target) { res[key] = deepClone(target[key]); }; return res; };
const colors = ['red', 'yellow', ['green', 'black']]; const colors1 = deepClone(colors);
colors1[2][1] = 'pink'; console.log(colors); console.log(colors1);
let person = { name: 'Katrina', age: 18, hobbies: ['run', 'swim', 'play football'], love: function() { console.log('She is a cool girl!') }, };
let person1 = deepClone(person);
person1.hobbies[2] = 'play tennies'; console.log(person1.hobbies); console.log(person.hobbies);
|
拓展:赋值和拷贝
拷贝区别于赋值最重要的一点是:拷贝会在堆上创建一个新的对象
示意图如下图所示:
|
和原对象是否指向同一地址 |
第一层数据为基本数据类型 |
原数据中包含子对象 |
赋值 |
是 |
改变会使原数据一同改变 |
改变会使原数据一同改变 |
浅拷贝 |
否 |
改变不会使原数据一同改变 |
改变会使原数据一同改变 |
深拷贝 |
否 |
改变不会使原数据一同改变 |
改变不会使原数据一同改变 |
参考
深入理解JS深浅拷贝
赋值和拷贝