跳到主要内容

11 篇博文 含有标签「技术」

查看所有标签

· 阅读需 4 分钟
Meoo

思路

从最基本的拷贝开始,一步一步处理更复杂的对象、类型。

拷贝对象字面量

先考虑基本类型和层层对象的情况,直接 for-in 遍历 + 递归拷贝

function deepClone(obj) {
// 基本类型直接返回
if (typeof obj !== 'object') return obj;
const newObj = {};
for (let key in obj) {
// 只复制自身的对象
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}

解决循环引用问题

可以用 Map 或 WeakMap 解决,对复杂数据、对象字面量进行深拷贝前保存其引用,如果下次又出现了该值,就不再进行拷贝,直接返回保存的值。

function deepClone(obj, hash = new WeakMap()) {
if (typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);

const newObj = {};
hash.set(obj, newObj);

for (let key in obj) {
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key], hash);
}
}
return newObj;
}

现在已经支持复制这样的对象 o 了:

const o = {
name: 'Lee',
attrs: {
degree: 'BA',
year: 2019
}
};
const b = {};
o.reference = b;
b.reference = o;

处理构造出的且可直接赋值的对象

一些构造出来的对象,例如 Array 和 Object 的实例,可能是 [1,2,3] 或者 { a:1, b:2},他们支持 for-in 遍历,而且可以 obj[key] = value 这样赋值,就拿他们的构造函数 new 出一个新对象,然后放在 for-in 遍历里面拷贝每一个值即可。

function deepClone(obj, hash = new WeakMap()) {
if (typeof obj !== 'object') return obj;

if (hash.has(obj)) return hash.get(obj);

// 对象本身没 constructor 属性,但可以访问到,因为会沿着原型链往上找
// 如果是 {} ,就相当于 new Object();如果是数组,就相当于 new Array()
let newObj = new obj.constructor;
hash.set(obj, newObj);

for (let key in obj) {
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key], hash);
}
}
return newObj;
}

处理其他常见类型

考虑 Date、RegExp、Set、Map、null 这些对象,需要用不同的手段来拷贝其值。

当对象是以下情况:

  1. null 对象直接返回 null
  2. Date 和 RegExp 对象,直接用构造函数包裹,new 出来返回即可
  3. Set 和 Map 集合,需要遍历每个值,并对每个值进行递归拷贝,最后返回新的集合

最终代码

function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Map) {
const o = new Map();
obj.forEach((val, key) => {
o.set(key, deepClone(val, hash));
});
return o;
}
if (obj instanceof Set) {
const s = new Set();
obj.forEach(v => {
s.add(v);
});
return s;
}

// 如果不是对象,直接返回
if (typeof obj !== 'object') return obj;

if (hash.has(obj)) return hash.get(obj); // 3

// obj 是对象字面量或构造出的对象,比如 Array、Object 的实例 ([]和{})
let newObj = new obj.constructor;
// 将对象储存在weakMap中
hash.set(obj, newObj); // 1
// 递归地把原对象属性复制给创建的对象
for (let key in obj) {
if (Object.hasOwn(obj, key)) {
newObj[key] = deepClone(obj[key], hash); // 2
}
}
return newObj;
}

测试对象:

const obj = {
set: new Set([1, 2, 4]),
map: new Map([[1, '123', 2, '456']]),
arr: [1, 3, 5],
attr: {
a: 1, b: 2
},
date: new Date(),
reg: new RegExp(),
fun: function () {
console.log('fn');
}
};

· 阅读需 2 分钟
Meoo

为什么防抖这样写不行?

function debounce(fn, delay) {
let timeout;
return function () {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(fn, delay, ...arguments);
};
}

· 阅读需 5 分钟
Meoo

负责人布置了个任务,写一个过滤 JSON 数据的方法,处理一下模型数据。递归绕的我真晕,脑子内存还是不太够。

样例数据

规则:bosclass 值为 boqs 的是树节点,bosclass 值为 wps 的是纯数据。如果树节点的 children 是空数组,那么把树节点移除掉。树的层级是无法预估的,可能有很多层。以下是过滤后的数据:

[
{
"bosclass": "boqs",
"children": [
{
"bosclass": "boqs",
"children": [
{
"bosclass": "wps",
"guid": "fa01a4a1f1d448a3bfb315dac41e1914"
},
{
"bosclass": "wps",
"guid": "cbf47dc52b2243c1a9d46bb046d8a13d"
}
],
"guid": "f785b4a061714dfe8fea367d12ddd29c"
}
],
"guid": "8bc6c9026bb9472f8a3e23b382f87ac1"
}
]

如何过滤呢?首先想到的方法是递归!

· 阅读需 3 分钟
Meoo

当需要根据一个已有分支,来创建一个新分支并在上面开发时,这篇内容会很有用。

一般情景是,在 dev 分支开发,此时遇到了一些 bug 需要修复,要根据 dev 分支创建一个 bugfix 分支,在此分支把问题解决,然后推送并发起 pull request 将代码合并到 dev 分支。

步骤

1.新建分支

依据:git checkout -b <new-branch> [<start-point>],创建并切换到 new-branch 分支,如果写上了 start-point 参数,则新分支的 HEAD 将指向它,不写则需要手动设置上游。

start-point:The new branch head will point to this commit. It may be given as a branch name, a commit-id, or a tag. If this option is omitted, the current HEAD will be used instead.

现在以远程的 origin/dev 分支为上游,新建了一个 bugfix 分支。

git checkout -b bugfix origin/dev

2.代码开发

在 bugfix 分支进行开发。

3.设置上游分支

现在的上游是 origin/dev,推送时会推到远程 dev 分支。 设置上游可以根据这条命令 git branch -u, --set-upstream-to <upstream>-u 简写比较方便,也可以使用完整参数 --set-upstream-to

git branch -u origin/bugfix

但是这里还不能这样用,因为还没有把本地创建的新分支推送到远程,无法设置远程的上游。因此需要推送并设置上游

git push -u origin bugfix

4.推送

可以推送到远程仓库

git push

如果没手动设置上游,需要次次都手动推送到指定分支

git push origin bugfix

在这之后,就可以在仓库看到了 bugfix 分支,可以发起合并请求了

· 阅读需 5 分钟
Meoo

Git Rebase 自己压根没用过,正巧公司团队要用到,学习总结一下。

使用场景

合并提交:完成一个 feature 提交了很多次,有很多杂乱连续的 commit,团队对提交规范要求一个特性对应一次提交,此时需要合并多个提交为一个。

分支变基:多人协作开发时,用 merge 合并分支会多出一个 Merge Commit 的提交,这个提交是两个分支的最新节点的合并项。分支树呈现的是一条线从 main 分支叉出去,经过几个提交,又汇合到 main,汇合处形成了一个提交节点。团队想要让提交记录是线性的,也不想要这个多余的提交,此时适合进行变基。

举例

有一个提交结点树,C1是首次提交,C4是 hotfix 分支第一次提交。主线是 main 分支。

               C4 <- C5
/
C1 <- C2 <- C3 <- C6