如何给对象设置默认值

1.用 … 解构设置

在看compressor.js的时候,他用一个文件来存默认值,然后导出,最后用解构的方式,后面的值覆盖前面的方式设置默认值。当然也可以用Object.assign都是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// defaults.js
export default {
maxWidth: Infinity,
maxHeight: Infinity,
// ...
}

// index.js
import DEFAULTS from './defaults';

export default class Compressor {
constructor (option) {
this.option = {
...DEFAULTS,
...option,
}
}
}

上面的方法看着挺方便的,但是还有缺陷,解决不了下面的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let user = {
a: "a1",
b: {
c: "c1"
}
}

let defaults = {
a: "a2",
b: {
c: "c2",
d: "d1" // 丢失了
},
}

let option = {
...defaults,
...user
}

console.log(option) // { a: 'a1', b: { c: 'c1' } }

可以看到默认配置中 d丢失了,下面的方法可以解决这个问题

2.extend方法

Mescroll.js实现的方法,适合多个方法都需要默认值的时候,并且可以深度匹配默认值,默认值作为第二个参数传入,

1
2
3
4
5
6
7
8
9
10
11
MeScroll.extend = function (userOption, defaultOption) {
if (!userOption) return defaultOption;
for (var key in defaultOption) {
if (userOption[key] == null) { // 如果用户没设置,则用默认值
userOption[key] = defaultOption[key];
} else if (typeof userOption[key] === 'object') {
MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
}
}
return userOption;
}

但是有些场景我们是需要有多个配置进行合并,我们可以参考jquery的extend实现方式

3.$.extend

$.extend([deep,] [target,] object1 [,objectN])

deep: Boolen类型,可选,表示是否进行递归合并(深/浅复制),为true是为深复制;默认值为false,浅复制。
target:扩展对象,可选,将接收新的属性。
objectN:一个对象,包含额外的属性,扩展到目标对象(扩展对象)。

我们可以砍掉一个没必要的功能,就是$.extend如果参数中只有一个对象,则扩展jquery对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 此方法会改变原对象
jQuery.extend = jQuery.fn.extend = function () {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1, // i表示从第几个参数开始向目标参数进行合并,默认从第2个参数开始向第1个参数进行合并
length = arguments.length,
deep = false; // 默认为浅度拷贝

// 如果第一个参数是用来设置deep的boolean值,则将target设置为下一个参数
if (typeof target === "boolean") {
deep = target;

target = arguments[i] || {};
i++;
}

// 判断目标参数的类型,若目标参数既不是object类型,也不是function类型,则为目标参数重新赋值
if (typeof target !== "object" && typeof target !== "function") {
target = {};
}

// 从第i个参数开始
for (; i < length; i++) {
// 获取第i个不为null或undefined的参数
if ((options = arguments[i]) != null) {

for (name in options) {
src = target[name];
copy = options[name];

// 防止循环引用
if (name === "__proto__" || target === copy) {
continue;
}

// 若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深合并
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
// 如果copy是数组类型,则判断目标参数中name字段的值是否存在,若存在则使用原来的,否则进行初始化
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;

target[name] = merge(deep, clone, copy);

// deep为false,表示浅拷贝,直接进行赋值
// 或者copy不为Object对象、数组,则直接赋值
} else if (copy !== undefined) {
// 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性
target[name] = copy;
}
}
}
}

// 返回修改后的目标参数
return target;
};

附1:jquery重构了的一段代码

1
2
3
4
5
6
7
8
9
10
11
12
if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}

target[name] = jQuery.extend(deep, clone, copy);
} else if ( copy !== undefined ) {
target[name] = copy;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;

target[name] = merge(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}

代码明显下面好看了许多,可以学习一下这种重构方式

附2:isEmptyObject、isPlainObject

1.isEmptyObject

1
2
3
4
5
6
7
8
9
10
11
isEmptyObject: function (obj) {
var name;

for (name in obj) {
return false;
}
return true;
}

// 还可以利用JSON.stringify
JSON.stringify(obj) == "{}"

2.isPlainObject

1
2
3
4
5
6
7
8
9
10
11
12
function isPlainObject (target) {
// 1.先排除明显不是Object的
if (!target || Object.prototype.toString.call(target) !== "[object Object]") return false;

// 2.判断是否有原型,没有原型的是简单对象
const proto = Object.getPrototypeOf(target);
if (!proto) return true;

// 3.判断其构造函数是否为 Object
const ctor = proto.constructor;
return typeof ctor === "function" && ctor === Object;
}

参考

https://blog.csdn.net/weixin_33694172/article/details/89369987

作者

Liang

发布于

2021-05-06

更新于

2021-12-24

许可协议


评论