1、深拷贝与浅拷贝
1.1 什么是深拷贝与浅拷贝
在了解深浅拷贝之前我们需要了解一下都有哪些数据类型
js中数据类型主要分为两大类:基本数据类型与引用数据类型
基本数据类型(值类型):字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol
引用数据类型:对象(Object)、数组(Array)、函数(Function)
基本数据类型:数据直接存储在栈(stack)中的数据。
引用数据类型:地址直接存储在栈(stack)中,真实的数据存储在堆内存中。当需要取引用类型数据时,会先检索栈中地址,取得地址后再去堆中获取真实的数据。
浅拷贝:会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用地址,当引用地址指向的值改变时也会跟着变化。
对于复杂数据类型来说:浅拷贝后的变量改变影响旧变量,未实现完全的隔离。
但对于基本数据类型数据没有啥影响
浅拷贝方法:for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 、=(赋值操作)等
深拷贝:深拷贝和浅拷贝是针对复杂数据类型(对象及数组)来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。
深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
.2 浅拷贝实现方式
浅拷贝是我们在使用基础数据类型时最常用到的一个方式,浅拷贝方法:for in、 Object.assign、 扩展运算符 ... 、Array.prototype.slice()、Array.prototype.concat() 、=(赋值操作)等
1、Object.assign
这里我们可以看到当只有一层的时候,修改一个变量另一个不受影响
但是二层之后,修改一个变量的值另一个也受到影响,因此是浅拷贝(以下几个方法也是同理)
let arr = ['张三',{name: '小明'}, {name: '小红'}]
let arr1 = Object.assign({},arr)
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
2、扩展运算符 ...
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = [...arr]
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
3、Array.prototype.slice()
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = arr.slice()
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
4、Array.prototype.concat()
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = arr.concat()
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
5、for in 与 =(赋值操作)
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = []
for (let key in arr) {
arr1[key] = arr[key];
}
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
3 深拷贝实现方式
深拷贝主要针对的是复杂数据类型(对象、数据、数组对象)
深拷贝常用的实现:JSON.parse(JSON.stringify(obj)) 、递归实现、函数库lodash
3.1、JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))是最简单的实现深拷贝的方法,直接拿来就能用
但是也有一个固有的缺陷:
1、对象的属性值是函数时,无法拷贝
2、原型链上的属性无法拷贝
3、会忽略undefined、symbol
4、不能处理RegExp、Date类型数据
let arr = [{
name:'小明'
},{
name:'小红'
}]
let arr1 = JSON.parse(JSON.stringify(arr))
arr1[0].name = '我换名字了'
console.log(arr,arr1);
实现效果:拷贝之后,变量之间不相互影响
3.2、递归实现(尽量能手写)
如果是基本数据类型就直接赋值操作,如果不是基本数据类型就继续递归调用
// 递归方法
function deepClone(source) {
// [] => Array(基类) {} => Object(基类)
// constructor 构造器
const targetObj = source.constructor === Array ? [] : {}
for (let keys in source) {
// key是否存在
if (Object.hasOwnProperty.call(source, keys)) {
// 引用数据类型
if (source[keys] && typeof source[keys] === 'object') {
// 递归
targetObj[keys] = deepClone(source[keys])
} else {
// 基础类 直接赋值
targetObj[keys] = source[keys]
}
}
}
return targetObj
}
let arr = [{
name: '小明'
}, {
name: '小红'
}]
let arr1 = deepClone(arr)
arr1[0].name = '我换名字了'
console.log(arr, arr1);
运行效果:
3.3、使用函数库lodash中的cloneDeep
Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。 Lodash 的模块化方法 非常适用于:
- 遍历 array、object 和 string
- 对值进行操作和检测
- 创建符合功能的函数
使用lodash
1、安装lodash
npm i --save lodash
2、按需引入
import _ from "lodash"
3、使用
let arr = ['张三', { name: '小明' }, { name: '小红' }]
let arr1 = _.cloneDeep(arr)
arr1[0] = '我换名字了'
arr1[1].name = '我又换名字了'
console.log(arr, arr1);
运行结果:两个变量互不影响是深拷贝
参考链接:
Lodash 简介 | Lodash中文文档 | Lodash中文网
2、如何让谷歌浏览器支持小字体
谷歌浏览器默认的字体最小12px,如果不去调浏览器配置,小于12px的字体默认展示是12px。如何用代码实现
scale(参数)缩放实现小字体
参数:大于1放大;小于1缩小;等于1不变
<div class="parent">
大字体
<div class="small-font">小字体</div>
</div>
<style>
.parent{
font-size:12px;
}
.small-font{
transform:scale(0.8);
-webkit-transform:scale(0.8);
}
</style>
3、字面量与Object.create()的区别
Object.create()静态方法以一个现有对象作为原型,创建一个新对象。
Object.create(proto, propertiesObject)
proto 必填,可以是null也可以是对象
propertiesObject 不必填,默认属性有value、writable(不可写)、configurable(可配置)、enumerable(可枚举)
Object.create(null) 创建的对象无原型,等价于
o = Object.create(null);
// 等价于:
o = { __proto__: null };
Object.create(obj) 创建的对象以obj对象为原型,这样就可以保证新创建的对象和原有对象解耦,当我们操作对象b或者c时并不会影响原有数据
propertiesObject
o2 = Object.create(
{},
{
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true,
},
},
);
// 这与以下语句不等价:
// o2 = Object.create({ p: 42 })
// 后者将创建一个原型为 { p: 42 } 的对象。
propertiesObject设置只读的时候,对象中键值不可改
个人总结:
1、Object.create(null)创建的对象,无原型比较纯洁
2、Object.create()可以实现一个继承目标且可高度配置的对象
3、字面量方式创建比较简洁
4、实现树形结构数据筛选
this.treeList = this.filterTree(this.listQuery.keyword, res.data)
filterTree(searchValue, searchTopicCatalogs) {
let tempSearchArr = [];
searchTopicCatalogs.forEach((treeNode) => {
if (treeNode.baseMenuEntityList && treeNode.baseMenuEntityList.length > 0) {
// 后序遍历
let tempArr = this.filterTree(searchValue, treeNode.baseMenuEntityList)||[];
// 当前树节点的子节点存在匹配字符,直接纳入数组
if (tempArr.length > 0) {
treeNode.baseMenuEntityList = tempArr
tempSearchArr.push(treeNode);
//当前树节点存在匹配字符
}else {
if (treeNode.menuName.indexOf(searchValue) >= 0) {
tempSearchArr.push(treeNode);
}
}
// 叶子节点
}else {
// 当前树节点存在匹配字符
if (treeNode.menuName.indexOf(searchValue) >= 0) {
tempSearchArr.push(treeNode);
}
}
});
return tempSearchArr;
},
实现结果: