1.用 ... 解构设置
在看compressor.js的时候,他用一个文件来存默认值,然后导出,最后用解构的方式,后面的值覆盖前面的方式设置默认值。当然也可以用Object.assign都是一样的
// defaults.js
export default {
maxWidth: Infinity,
maxHeight: Infinity,
// ...
}
// index.js
import DEFAULTS from './defaults';
export default class Compressor {
constructor (option) {
this.option = {
...DEFAULTS,
...option,
}
}
}
上面的方法看着挺方便的,但是还有缺陷,解决不了下面的问题
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实现的方法,适合多个方法都需要默认值的时候,并且可以深度匹配默认值,默认值作为第二个参数传入,
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对象。
// 此方法会改变原对象
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重构了的一段代码
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;
}
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
isEmptyObject: function (obj) {
var name;
for (name in obj) {
return false;
}
return true;
}
// 还可以利用JSON.stringify
JSON.stringify(obj) == "{}"
2.isPlainObject
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;
}