- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第36期,链接:github.com/benjycui/om…
1.omit.js原理分析
原理:在不影响原数据下,剔除对象中的属性。先拷贝出来一个对象(浅拷贝),然后进行操作。
function omit(obj, fields) {
// Object.assign--浅拷贝,第一层数据深拷贝,第二层嵌套数据为浅拷贝
const shallowCopy = Object.assign({}, obj);
for (let i = 0; i < fields.length; i += 1) {
const key = fields[i];
delete shallowCopy[key];
}
return shallowCopy;
}
2.深拷贝和浅拷贝的区别?为啥会有深拷贝和浅拷贝?分别实现的方法是什么?
- 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。 浅拷贝:只进行一层拷贝,深拷贝就是无限层级的拷贝。
- 深拷贝和浅拷贝区别只是针对引用数据类型。
- 浅拷贝的实现方式
1. Object.assign() //对非嵌套对象进行深拷贝的方法
`当object只有一层的时候,是深拷贝`
2. Array.prototype.concat() //适合一维数组
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
3. Array.prototype.slice() //适合一维数组
4. for...in 循环遍历赋值
5. 扩展运算符(最常用的方式)
- 深拷贝的实现方式
1. JSON.parse(JSON.stringify())
缺点:
1. 不能处理函数
2. 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象
3. 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象
4. 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失
5. 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
6. JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象constructor
function Person(name) {
this.name = name;
console.log(name)
}
const liai = new Person('liai');
const test = {
name: 'a',
date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
7. 如果对象中存在循环引用的情况也无法正确实现深拷贝
8. 不支持Symbol
2. 手写克隆函数
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
function deepCopy(souce) {
if (!isObject(souce)) return souce;
let target = Array.isArray(souce) ? [] : {};
for( var k in souce) {
if (souce.hasOwnProperty(k)) {
if (souce[k] && typeof souce[k] === 'object') {
target[k] = deepCopy(souce[k]);
} else {
target[k] = souce[k];
}
}
}
return target;
}
1. `上述函数,当数据的层次很深,会栈溢出`?
2. `循环引用`
const symbolName = Symbol();
const obj = {
objNumber: new Number(1),
number: 1,
objString: new String('ss'),
string: 'stirng',
objRegexp: new RegExp('\w'),
regexp: /w+/g,
date: new Date(),
function: function () {},
array: [{a: 1}, 2],
[symbolName]: 111
}
obj.d = obj;
const isObject = obj => obj !== null && (typeof obj === 'object' || typeof obj === 'function');
const isFunction = obj => typeof obj === 'function'
function deepClone (obj, hash = new WeakMap()) {
if (hash.get(obj)) {
// 环处理
return hash.get(obj);
}
if (!isObject(obj)) {
return obj;
}
if (isFunction(obj)) {
// function返回原引用
return obj;
}
let cloneObj;
const Constructor = obj.constructor;
switch (Constructor) {
case Boolean:
case Date:
return new Date(+obj);
case Number:
case String:
case RegExp:
return new Constructor(obj);
default:
cloneObj = new Constructor();
hash.set(obj, cloneObj);
}
[...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)].forEach(k => {
cloneObj[k] = deepClone(obj[k], hash);
})
return cloneObj;
}
const o = deepClone(obj)
3. 函数库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);
4. $.extend([deep],target,object1,...)
3.如果传入的第二个参数不是数组呢?
解决方法: 将任何数据转换成数组 arrify---33期源码分析中使用。
4.可以通过其他方式实现,改造此函数,多些思考。
function omit(obj, fields) {
// Object.assign--浅拷贝,第一层数据深拷贝,第二层嵌套数据为浅拷贝
const shallowCopy = Object.assign({}, obj);
// const shallowCopy = {...obj}; ---浅拷贝中的方法都可以
let arr=[];
if(Array.isArray(fields)){
arr=fields
}else{
arr=arrify(fields)
}
//数组循环的三种方法:for..in forEach for
for (let i = 0; i < fields.length; i += 1) {
const key = fields[i];
delete shallowCopy[key];
}
return shallowCopy;
}
5.感悟
第一次在掘金上发表笔记,原来一直在本地做一些记录,希望有错误及时指出,大家一起努力。