以前在开发中有遇到拷贝的问题,我也看到刚入门同窗在用vue开发时所写的赋值相关问题,在使用时,概念使得不清楚使赋值数据模糊不清。为了帮助他人或者帮助本身更能深入记住,在查阅了相关资料以后在此记录下本身的小小看法。在弄清楚拷贝以前咱们先弄清楚数据类型和堆栈的关系
JS数据类型:JS 的数据类型有几种?javascript
8种,Number、String、Boolean、Null、undefined、object、symbol、bigInt
Symbol 本质上是一种惟一标识符,可用做对象的惟一属性名,这样其余人就不会改写或覆盖你设置的属性值html
Symbol 数据类型的特色是惟一性,即便是用同一个变量生成的值也不相等前端
let id1 = Symbol('id'); let id2 = Symbol('id'); console.log(id1 == id2); //false
Symbol 数据类型的另外一特色是隐藏性,for···in,object.keys() 不能访问vue
let id = Symbol("id"); let obj = { [id]:'symbol' }; for(let option in obj){ console.log(obj[option]); //空 }
可是也有可以访问的方法:Object.getOwnPropertySymbols,Object.getOwnPropertySymbols 方法会返回一个数组,成员是当前对象的全部用做属性名的 Symbol 值java
JS数据类型:Object 中包含了哪几种类型?jquery
其中包含了Object,Array,Date,Function,RegExp等
JS数据类型:JS的基本类型有哪些呢?git
基本类型(单类型):String、Number、boolean、null、undefined
谷歌67版本中出现了一种bigInt,是指安全存储、操做大整数。(可是不少人不把这个作为一个类型)BigInt
数据类型的目的是比Number
数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤其重要。使用BigInt
,整数溢出将再也不是问题。具体请看JavaScript 标准内置对象, JS最新基本数据类型:BigIntgithub
基本类型:基本类型值在内存中占据固定大小,保存在`栈内存`中(不包含`闭包`中的变量) 引用类型:引用类型的值是对象,保存在`堆内存`中。而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址(引用),引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中得到实体。
这里用一张图代表它们以前储存的关系数组
闭包中的变量并不保存中栈内存中,而是保存在堆内存中。 这也就解释了函数调用以后以后为何闭包还能引用到函数内的变量。
咱们先来看什么是闭包:安全
function A() { let a = '羊先生' function B() { console.log(a) } return B }
函数 A 返回了一个函数 B,而且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。
函数 A 调用后,函数 A 中的变量这时候是存储在堆上的,因此函数B依旧能引用到函数A中的变量
let a ='羊先生'; let b = a; b='www.vipbic.com'; console.log(a); // 羊先生
结论:在栈内存中的数据发生数据变化的时候,系统会自动为新的变量分配一个新的之值在栈内存中,两个变量相互独立,互不影响的。
let a = {x:'羊先生', y:'羊先生1'} let b = a; b.x = 'www.vipbic.com'; console.log(a.x); // www.vipbic.com
结论 引用类型的复制,一样为新的变量b分配一个新的值,保存在栈内存中,不一样的是这个变量对应的具体值不在栈中,栈中只是一个地址指针。两个变量地址指针相同,指向堆内存中的对象,所以b.x发生改变的时候,a.x也发生了改变
Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之因此把它放在浅拷贝里,是由于它看起来像是深拷贝。而实际上它是浅拷贝。原数组的元素会按照下述规则拷贝:
var a = [ 1, 3, 5, { x: 1 } ]; var b = Array.prototype.slice.call(a); b[0] = 2; console.log(a); // [ 1, 3, 5, { x: 1 } ]; console.log(b); // [ 2, 3, 5, { x: 1 } ];
从输出结果能够看出,浅拷贝后,数组a[0]并不会随着b[0]改变而改变,说明a和b在栈内存中引用地址并不相同。
var a = [ 1, 3, 5, { x: 1 } ]; var b = Array.prototype.slice.call(a); b[3].x = 2; console.log(a); // [ 1, 3, 5, { x: 2 } ]; console.log(b); // [ 1, 3, 5, { x: 2 } ];
从输出结果能够看出,浅拷贝后,数组中对象的属性会根据修改而改变,说明浅拷贝的时候拷贝的已存在对象的对象的属性引用
语法: var cloneObj = { ...obj };
let obj = {a:1,b:{c:1}} let obj2 = {...obj}; obj.a=2; console.log(obj); //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}} obj.b.c = 2; console.log(obj); //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
扩展运算符也是浅拷贝,对于值是对象的属性没法彻底拷贝成2个不一样对象
function shallowClone(copyObj) { var obj = {}; for ( var i in copyObj) { obj[i] = copyObj[i]; } return obj; } var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = shallowClone(x); console.log(y.b.f === x.b.f); // true
object.assign() 方法能够把任意多个的源对象自身的可枚举属性拷贝给目标对象,而后返回目标对象。
var obj1 = { title: '测试', subset: { name: '子对象', subpoll: { des: '子子对象' }, sex: function() { console.log('sex') } }, age: function() { console.log('age') } } //只从表面上来看,彷佛Object.assign()的目标对象是{},是一个新的对象(开辟了一块新的内存空间),是深拷贝 var ojb2 = Object.assign({}, obj1) ojb2.title = '1111' // obj1 不受影响 ojb2.subset.name = '2222' // obj1 受影响 ojb2.subset.subpoll.des = '3333' // obj1 受影响 // 打印看结果 console.log('obj1:', obj1) console.log('ojb2:', ojb2)
从打印结果能够看出,Object.assign
是一个浅拷贝,它只是在根属性(对象的第一层级)建立了一个新的对象,可是对于属性的值是对象的话只会拷贝一份相同的内存地址。
Object.assign注意事项
1.只拷贝源对象的自身属性(不拷贝继承属性)
2.它不会拷贝对象不可枚举的属性
3.undefined
和null
没法转成对象,它们不能做为Object.assign
参数,可是能够做为源对象
Object.assign(undefined) // 报错 Object.assign(null) // 报错 let obj = {a: 1}; Object.assign(obj, undefined) === obj // true Object.assign(obj, null) === obj // true
4.属性名为Symbol
值的属性,能够被Object.assign拷贝
JSON.stringify()是前端开发过程当中比较经常使用的深拷贝方式。原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象
let arr = [1, 3, { username: '羊先生' }]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'www.vipbic.com'; console.log(arr4);// [ 1, 3, { username: 'www.vipbic.com' } ] console.log(arr);// [ 1, 3, { username: ' 羊先生' } ]
实现了深拷贝,当改变数组中对象的值时候,原数组中的内容并无发生改变。JSON.stringify()虽然能够实现深拷贝,可是还有一些弊端好比不能处理函数等。
JSON.stringify()实现深拷贝注意点
1.拷贝的对象的值中若是有函数,undefined,symbol则通过JSON.stringify()序列化后的JSON字符串中这个键值对会消失
2.没法拷贝不可枚举的属性,没法拷贝对象的原型链
3.拷贝Date引用类型会变成字符串
4.拷贝RegExp引用类型会变成空对象
5.对象中含有NaN、Infinity和-Infinity,则序列化的结果会变成null
6.没法拷贝对象的循环应用(即obj[key] = obj)
总结:它会抛弃对象的constructor,深拷贝以后,无论这个对象原来的构造函数是什么,在深拷贝以后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有能够转成JSON格式的对象才能够这样用,像function没办法转成JSON;
这是看github一个栗子,https://github.com/wengjq/Blo...
//类型判断 (function($) { "use strict"; var types = "Array,Object,String,Date,RegExp,Function,Boolean,Number,Null,Undefined".split(","); for (let i = types.length; i--; ) { $["is" + types[i]] = str => Object.prototype.toString.call(str).slice(8, -1) === types[i]; } return $; })(window.$ || (window.$ = {})); function copy(obj, deep = false, hash = new WeakMap()) { if (hash.has(obj)) { return hash.get(obj); } if ($.isFunction(obj)) { return new Function("return " + obj.toString())(); } else if (obj === null || typeof obj !== "object") { return obj; } else { var name, target = $.isArray(obj) ? [] : {}, value; hash.set(obj, target); for (name in obj) { value = obj[name]; if (deep) { if ($.isArray(value) || $.isObject(value)) { target[name] = copy(value, deep, hash); } else if ($.isFunction(value)) { target[name] = new Function("return " + value.toString())(); } else { target[name] = value; } } else { target[name] = value; } } return target; } } //使用 var x = {}; var y = {}; x.i = y; y.i = x; var z = copy(x, true); console.log(x, z); var a = {}; a.a = a; var b = copy(a, true); console.log(a, b);
该函数库也有提供_.cloneDeep用来作 Deep Copy(lodash是一个不错的第三方开源库,有好多不错的函数,也能够看具体的实现源码)
var _ = require('lodash'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f); // false
jquery 提供一个$.extend
能够用来作深拷贝
语法:$.extend([deep], target, object1[, objectN] )
deep:表示是否深拷贝 默认为false 为true为深拷贝,为false,则为浅拷贝
target: Object类型 目标对象,其余对象的成员属性将被附加到该对象上。
object1 objectN: 可选 Object类型 第一个以及第N个被合并的对象。
var $ = require('jquery'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f); // false
JavaScript中的 Object 类型
javaScript中浅拷贝和深拷贝的实现
深拷贝与浅拷贝的实现