每日一句
You need to face your fears.
释义:你需要直面你的恐惧。
前言
- javascript中数据类型:基本类型和引用类型
栈区(stack):自动分配的内存空间,会被系统自动回收。
堆区(heap):动态分配的内存空间,大小不定,不会被系统回收。
基本类型:
String, Number, Boolean, Undefined, Null, Sysmbol
按值传递
一般存在内存的栈区,存取快,存量小
引用类型:
Array, Object, Function, Date, RegExp, ...
按引用传递
一般存在内存中的堆区,存取慢,存量大,栈中存放内存地址,指向引用的本身
下图非常清晰的展示了基本类型与引用类型在内存中存放形式。
const xiaoming = {
name: '小明',
age: 30,
salary: 8000
}
// 简单赋值
const xiaohong = xiaoming
xiaohong.name = '小红'
xiaohong.salary = 10000
console.log(xiaohong) // { "name": "小红", "age": 30, "salary": 10000 }
console.log(xiaoming) // { "name": "小红", "age": 30, "salary": 10000 }
以上代码实现了浅拷贝,虽然小红可以复制小明的属性,然后只需改变小红的name和salary,这无形之间把小明的也给变了,这哪行呢?
怎样更好的复制对象呢?这里用到了浅拷贝和深拷贝
浅拷贝和深拷贝是相对于引用类型而言的:
浅拷贝: 指两个js对象指向同一个内存地址,其中一个改变会影响另一个;
深拷贝: 指复制后的新对象重新指向一个新的内存地址,两个对象改变互不影响。
浅拷贝
概念与运用
「只拷贝了数据对象的第一层,深层次的数据值与原始数据会互相影响(拷贝后的数据与原始数据还存有关联)」
常见浅拷贝的方法:Object.assign()
、扩展运算符
const xiaoming = {
name: '小明',
age: 30,
salary: 8000
}
// const xiaohong = Object.assign({}, xiaoming)
const xiaohong = {...xiaoming}
xiaohong.name = '小红'
xiaohong.salary = 10000
console.log(xiaohong) // { "name": "小红", "age": 30, "salary": 10000 }
console.log(xiaoming) // { "name": "小明", "age": 30, "salary": 8000 }
当然还有通过for
,concat
, slice
, Array.from
, Array.of
等实现浅拷贝。
浅拷贝的简单实现
function shallowClone(obj) {
// 此仅考虑数组和对象两种情况
if (Array.isArray(obj)) {
let target = []
for(let i=0; i< obj.length; i++) {
target.push(i)
}
return target
} else if (typeof obj === 'object') {
let target = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
target[key] = obj[key]
}
}
return target
}
}
const xiaoming = {
name: '小明',
children: ['Tom', 'Sam']
}
xiaohong.name = '小红'
console.log(xiaohong , xiaoming) // { name: '小红', children: [ 'Tom', 'Sam' ] } { name: '小明', children: [ 'Tom', 'Sam' ] }
浅拷贝的问题
// 如果存在引用类型数据呢
const xiaoming = {
name: '小明',
children: ['Tom', 'Sam']
}
const xiaohong = {...xiaoming}
xiaohong.name = '小红'
xiaohong.children[0] = 'steven'
console.log(xiaohong) // { name: '小红', children: [ 'steven', 'Sam' ] }
console.log(xiaoming) // { name: '小明', children: [ 'steven', 'Sam' ] }
// 拷贝的是地址
console.log(xiaohong.children === xiaoming.children) // true
当拷贝的是引用类型,是拷贝其内存地址,所以改了就会影响原数据,这时候得需要考虑深拷贝了。
深拷贝
概念与运用
「不管数据对象有多少层,改变拷贝后的值都不会影响原始数据的值。(拷贝后的数据与原始数据毫无关系)」
常用的深拷贝方式:JSON.parse(JSON.stringify(obj))
const xiaoming = {
children: ['Tom', 'Sam']
}
const xiaohong = JSON.parse(JSON.stringify(xiaoming))
xiaohong.children[0] = 'Jacky'
console.log(xiaohong, xiaoming) // { children: [ 'Jacky', 'Sam' ] } { children: [ 'Tom', 'Sam' ] }
console.log(xiaohong === xiaoming) // false
MDN:
undefined、
任意的函数以及symbol
值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成null
(出现在数组中时)。
此方法用缺陷:无法正确处理函数
和正则
如果要彻底解决这些需要手写代码,或用开源的库像lodash等。
深拷贝实现
- 兼容数组和对象
function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
// 测试
const arr1 = [1,2,3, {a: {b: 8 }}]
const arr2 = clone(arr1)
arr2[3].a.b = 4
console.log(arr1[3].a , arr2[3].a) // { b: 8 } { b: 4 }
- 兼容函数
思路:1.首先需要返回一个新的函数 2.新的函数执行结果必须与原函数相同
function deepClone(obj){
let target = {}
if(obj instanceof Function){
target = function(){
// 在函数中去执行原来的函数,确保返回的值相同
return target.call(this, ...arguments);
}
}
}
- 兼容正则表达式
function deepClone(obj){
let target = {}
if(target instanceof RegExp){
target = new RegExp(obj.source,obj.flags);
}
}
- 兼容日期
function deepClone(obj){
let target = {}
if(target instanceof Date){
target = new Date(obj);
}
}
优化后:
function deepClone(obj){
if(obj instanceof Object){
let dist ;
if(obj instanceof Array){
// 拷贝数组
dist = [];
}else if(obj instanceof Function){
// 拷贝函数
dist = function () {
return obj.call(this, ...arguments);
};
}else if(obj instanceof RegExp){
// 拷贝正则表达式
dist = new RegExp(obj.source,obj.flags);
}else if(obj instanceof Date){
dist = new Date(obj);
}else{
// 拷贝普通对象
dist = {};
}
for(let key in obj){
// 过滤掉原型身上的属性
if (obj.hasOwnProperty(key)) {
dist[key] = deepClone(obj[key]);
}
}
return dist;
}else{
return obj;
}
}