基本数据类型和引用数据类型

JS数据分为基本数据类型和引用数据类型。基本数据类型的变量存储在栈中,引用数据类型则存储在堆中,引用数据类型的存储地址则保存在栈中。

下面来看一个小例子

    // 基本数据类型
let intType = 1;
console.log('初始intType:' + intType);
let copyIntType = intType;
copyIntType = 2;
console.log('更改后intType:' + intType);
console.log('更改后copyIntType:' + intType);
let object = {
a: 1
};
// 引用数据类型
let copyObject = object
console.log('初始object:');
console.log(object);
copyObject.a = 2;
console.log('更改后的object:');
console.log(object);
console.log('更改后的copyObject:');
console.log(copyObject);

结果:

基本数据类型在复制的时候会创建一个值的副本,并将该副本赋值给新变量。引用类型在复制的时候其实复制的是指针。

深浅拷贝

  • 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
  • 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响

浅拷贝

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。实现方式如下

实现

  • 遍历赋值实现
  • ES6扩展运算符
  • ES6方法Object.assign()
  • 数组方法(只适用于类数组对象)

    Array.from(arrayLike)

    Array.prototype.concat()

    Array.prototype.slice()

遍历赋值实现

var obj = { a:1, arr: [2,3] };
//浅拷贝实现
for (var prop in obj){
if(obj.hasOwnProperty(prop)){
shallowObj[prop] = obj[prop];
}
}

ES6扩展运算符

var obj = { a:1, arr: [2,3] };
var obj1 = {...obj}

ES6方法Object.assign()

var obj = { a:1, arr: [2,3] };
var obj1 = Object.assign({}, obj);

数组方法(仅适用于类数组对象)

Array.from(arrayLike)

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = Array.from(array1);

Array.prototype.concat()

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.concat([1,2]);

Array.prototype.slice()

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.slice(0,2);

引用赋值:地址的赋值,将对象指针赋值给一个变量,让此变量指向对象

深拷贝

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

实现方式如下:

  • JSON.parse()和JSON.stringify()
  • 递归

JSON.parse()和JSON.stringify()

    let parseObject = {
a: {
b: 1
}
}
let cloneParseObject = JSON.parse(JSON.stringify(parseObject));
parseObject.a.b = 2;
console.log('cloneParseObject');
console.log(cloneParseObject);

缺陷:

  • 会忽略 undefined
  • 会忽略 symbol
  • 无法对function进行处理 需要确认.
  • 不能解决循环引用的对象

递归(简单版)

// 深拷贝
function cloneDeep(obj) {
if (!obj && typeof obj !== 'object') {
throw new Error('错误参数');
}
const targetObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
//只对对象自有属性进行拷贝
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
targetObj[key] = cloneDeep(obj[key]);
} else {
targetObj[key] = obj[key];
}
}
}
return targetObj;
}

关键点

  • 判断参数类型
  • 判断是否是数组
  • for in遍历
  • 判断是否是自有对象
  • 判断子属性是否是对象,是对象则递归

递归(复杂版)

const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]'; const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]'; const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]; function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
} function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
} function getType(target) {
return Object.prototype.toString.call(target);
} function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
} function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
} function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
} function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
} function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target;
}
// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
return cloneTarget;
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
module.exports = {
clone
};

复杂递归实现详解:https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg

示例代码

深浅拷贝.html

参考

https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg

https://blog.csdn.net/a2013126370/article/details/89035722

JS深浅拷贝及其实现的更多相关文章

  1. js 深浅拷贝 笔记总结

    一.js 数据类型 javaScritp的数据类型有:数值类型.字符串类型.布尔类型.null.undefined.对象(数组.正则表达式.日期.函数),大致分成两种:基本数据类型和引用数据类型, 其 ...

  2. jQuery开发插件的两个方法 js 深浅拷贝

    1.jQuery.extend(object);为扩展jQuery类本身.为类添加新的方法.由全局函数来调用, 主要是用来拓展个全局函数 2.jQuery.fn.extend(object);为jQu ...

  3. js深浅拷贝

    作为一枚前段,我们知道对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况.通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况. 浅拷贝 首先可以通过O ...

  4. 最简js深浅拷贝说明

    1.浅拷贝 浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响.  浅拷贝分两种情况: 1.直接拷贝源对象的引用 2. 源对象拷贝实例,但其属性对象(类型为Object, ...

  5. JS 深浅拷贝

    首先理解概念 浅拷贝: 只复制对象的基本类型, 对象类型, 仍属于原来的引用. 深拷贝: 不紧复制对象的基本类, 同时也复制原对象中的对象.就是说完全是新对象产生的. 首先看浅拷贝 //浅拷贝 var ...

  6. JS中深浅拷贝 函数封装代码

    一.了解 基本数据类型保存在栈内存中,按值访问,引用数据类型保存在堆内存中,按址访问. 二.浅拷贝 浅拷贝只是复制了指向某个对象的指针,而不是复制对象本身,新旧对象其实是同一内存地址的数据,修改其中一 ...

  7. js 基础数据类型和引用类型 ,深浅拷贝问题,以及内存分配问题

    js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用 ...

  8. JS--变量及深浅拷贝

    JS变量分为基本类型和引用类型 基本类型数据包括Number, String, Boolean, Null, Undefined五种类型: 引用数据类型包括Array, Date, RegExp, F ...

  9. 【 js 基础 】 深浅拷贝

    underscore的源码中,有很多地方用到了 Array.prototype.slice() 方法,但是并没有传参,实际上只是为了返回数组的副本,例如 underscore 中 clone 的方法: ...

随机推荐

  1. XSSFWorkbook

    支持2007以后的 此类与HSSFWorkbook(支持2007之前) 类似,读取文件时把全部的内容都存放到内存中,关闭输入流后. 内存与硬盘完全是毫无关系的两份数据,所有的操作都是对内存的操作,最后 ...

  2. 【CSP2019】括号树 题解(递推+链表)

    前言:抽时间做了做这道题,把学长送退役的题. ----------------- 题目链接 题目大意:定义$()$是合法括号串.如果$A,B$是合法括号串,那么$(AB),AB$为合法括号串.现给定根 ...

  3. C++类、函数、指针

    1.初始化所有指针. 2. (1)指向常量的指针: (2)常量指针:指针本身为常量: 3.若循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环. 4.字符数组要注意字符串字面值 ...

  4. JVM系列之:JIT中的Virtual Call接口

    目录 简介 最常用的接口List 多个List的调用 不一样的List调用 总结 简介 上一篇文章我们讲解了Virtual Call的定义并举例分析了Virtual Call在父类和子类中的优化. J ...

  5. ios 生成字母加数字的随机数

    文章来自:http://blog.csdn.net/baidu_25743639/article/details/73801700 近期项目第三方登录之后默认创建账号和密码,就用随机数生产,这里只需要 ...

  6. 为什么overflow:hidden能达到清除浮动的目的?

    1. 什么是浮动 <精通CSS>(第3版)关于浮动的描述: 浮动盒子可以向左或向右移动,直到其外边沿接触包含块的外边沿,或接触另一个浮动盒子的外边沿. 浮动盒子也会脱离常规文档流,因此常规 ...

  7. cudnn加速计算

    cudnn加速运算 torch.backends.cudnn.enabled = True torch.backends.cudnn.benchmark = True 第一句话是说,使用的是非确定性算 ...

  8. C#LeetCode刷题之#443-压缩字符串​​​​​​​(String Compression)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3943 访问. 给定一组字符,使用原地算法将其压缩. 压缩后的长度 ...

  9. Web前端性能优化,应该怎么做?

    摘要:本文将分享一些前端性能优化的常用手段,包括减少请求次数.减小资源大小.各种缓存.预处理和长连接机制,以及代码方面的性能优化等方面. base64:尤其是在移动端,小图标可以base64(webp ...

  10. jQuery的基础效果题

    Jquery第二次考核 之真金不怕火炼 1.  名词解释 实例对象:var p1=new Person();  p1就是实例对象 构造:function Person(){} 原型对象:在 JavaS ...