数据类型
- 基本数据类型:简单的数据,保存基本数据类型的变量是按值进行访问的,因此直接保存在栈内存中
- 引用数据类型:保存在堆内存中,在栈内存中保存了一个堆内存中实际对象的引用,引用数据类型的变量是按照引用进行访问的
复制值
- 基本数据类型:在通过变量把一个基本数据赋值到另一个变量时,基本数据会被复制到新变量的位置
| 12
 
 | let num1 = 5;let num2 = num1;
 
 | 
 
从上图可知,num1和num2是独立使用的,互不干扰
- 引用数据类型:在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象,操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来
 
深浅拷贝
深浅拷贝都是针对于引用类型的,因为引用类型是存放在堆内存中的,在栈地址中有一个或者多个地址来指向堆内存的某一数据
浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象
浅拷贝是指被复制对象的所有变量都与原来的对象含有相同的值,而所有的对其他对象的引用仍然指向原来的对象
浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,如果你修改了“副本”的值,那么原来的对象也会被修改
浅拷贝只拷贝一层,深层次的引用类型则共享内存地址
 
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
深拷贝是一个整个独立对象的拷贝,深拷贝会拷贝所有的属性,并且拷贝属性指向的动态分配的内存
深拷贝把要复制的对象所引用的对象都复制了一遍,如果你修改了“副本”的值,那么原来的对象不会被修改,两者是相互独立的
 
实现浅拷贝的方式
- Object.assign
- Array.protptype.slice
- Array.protptype.concat
- 使用拓展运算符
- 手写浅拷贝
Object.assign
补课:Object.assign
| 12
 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
| 12
 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
| 12
 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);
 
 | 
拓展运算符
| 12
 3
 4
 5
 6
 7
 
 | const colors = ['red', 'yellow', ['green', 'black']];const colors1 = [...colors];
 
 
 colors1[2][1] = 'pink';
 console.log(colors);
 console.log(colors1);
 
 | 
手写浅拷贝
| 12
 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()
| 12
 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()
| 12
 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()
| 12
 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的存在
| 12
 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);
 
 | 
手写深拷贝
| 12
 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深浅拷贝
赋值和拷贝