背景
在日常开发中,有时候我们会给定一些默认的参数,这样使用者就可以少传一些参数,降低某块功能的使用难度。以下是某个场景:
const defaultOptions = {
name: '蔡徐坤',
actions: {
sing: true,
jump: true,
rap: true,
}
}
const myOptions = {
name: '周杰伦',
actions: {
jump: false,
lol: true
}
}
const result = fn(myOptions, defaultOptions)
console.log(result)
/*
{
name: '周杰伦',
actions: {
sing: true,
jump: false,
rap: true,
lol: true
}
}
*/
看出来这个fn
函数做了什么没?用户提供了属性的话就覆盖,否则使用缺省数据。今天我们就来实现这个fn
函数
lodash
lodash
这个库的使用率还是很高的,很多开发者多多少少用过。其提供了丰富多彩的功能函数,基本覆盖了我们的日常开发任务。很有名气的有:防抖 debounce、节流 throttle、深拷贝 cloneDeep等。
同时,lodash
也提供了解决我们今天问题的函数:defaultsDeep。
用起来,和我们上文的fn
也基本一致,下面是官方的一个实例:
_.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
// => { 'a': { 'b': 2, 'c': 3 } }
用法是一模一样,有兴趣的同学可以看看lodash
是怎么实现的。我们接着往下看,看看我们是怎么实现的
javascriptweekly 474期
在javascriptweekly 474期中向我们推荐了一个小工具,也是为了解决这个问题,他就是我们今天的主角defu,其貌不扬的仓库,地址。我们来看看这位jsless
(作者)是怎么完成的。
源码的量还是很少的,我们直接开门见山,打开lib/index.js
function isObject (val) {
return val !== null && typeof val === 'object' && !Array.isArray(val)
}
function defu (_obj, _defaults) {
if (!isObject(_obj)) {
return defu({}, _defaults)
}
if (!isObject(_defaults)) {
return defu(_obj, {})
}
var obj = Object.assign({}, _defaults)
Object.keys(_obj).forEach(function (key) {
if (key === '__proto__' || key === 'constructor') {
return
}
var val = _obj[key]
if (val === null) {
return
}
if (isObject(val) && isObject(obj[key])) {
obj[key] = defu(val, obj[key])
} else {
obj[key] = val
}
})
return obj
}
module.exports = defu
源码开头定义了个判断是否是对象的函数isObject
,不为null,typeof为object,不为数组。哈哈,在js中,很平常的一个判断是否为对象的功能函数。
defu
函数的核心思想就是使用递归去"分解"_obj
中的对象属性
,直到成为基本数据类型。
var obj = Object.assign({}, _defaults)
这里定义的obj
变量能保证_defaults
不会被修改,防止污染数据源。在迭代的过程中不停的修改obj
,最后return出去。
obj[key] = defu(val, obj[key])
这行代码是本库的核心代码,一字千金。通过递归defu
函数不停地分解_obj
。
总结
总体而言,这个库还是很小的,缺乏一些边缘场景的考虑。但是通过其,学习下怎么去使用递归思想
解决日常开发中的问题,还是很值得看看的。有兴趣的同学可以去看看_.defaultsDeep
的实现,对比下,还是能看出lodash
的全面和成熟!