# createAssigner
# Description
创建一个类似assign
的函数。主要用在 merge 和 mergeWith
# Params
assigner
- 合并值的函数。
# Return
Function
# Depend
import isIterateeCall from './isIterateeCall.js'
# Code
function createAssigner(assigner) {
return (object, ...sources) => {
let index = -1
let length = sources.length
let customizer = length > 1 ? sources[length - 1] : undefined
const guard = length > 2 ? sources[2] : undefined
customizer = (assigner.length > 3 && typeof customizer === 'function')
? (length--, customizer)
: undefined
if (guard && isIterateeCall(sources[0], sources[1], guard)) {
customizer = length < 3 ? undefined : customizer
length = 1
}
object = Object(object)
while (++index < length) {
const source = sources[index]
if (source) {
assigner(object, source, index, customizer)
}
}
return object
}
}
# Analyze
首先确定
customizer
函数是否存在,这里判断sources
的length
是否大于1 ,也就是说在customizer
函数之前至少是有一个 对象参与合并的,然后如果length
大于1,先取source
的最后一个值作为customizer
的值紧接着判断
assigner
的参数长度,也就是merge
和mergeWith
的区别,如果length
大于 3,说明是mergeWith
,是可以传入 自定义合并函数的如果同时 最先定义的
customizer
为function
类型 ,则会将sources
的length
减 1,也就是最后一个值为customizer
不参与合并,同时使用 逗号操作符 返回customizer
的值如果
assigner
参数的长度小于等于3,或者第一步获取的customizer
不是function
类型,则将customizer
置为undefined
判断
guard
,也就是判断sources[2]
的值,如果guard
存在,则同时判断 是不是属于某个迭代函数的参数,所以判断了sources[0]
,sources[1]
,sources[2]
是否满足isIterateeCall
如果满足了
isIterateeCall
的条件,则将length
置为 1,因为只需要拿到 迭代函数的第一个参数,也就是 当前的value
值即可,不需要后面的参数iteratee
函数 接受三个参数,当前值,当前值对应的key或者索引,原始数组或对象这里同时判断了
length < 3
的情况,因为在之前已经判断了customizer
函数是否存在,如果满足条件存在,length
已经减一了,这里判断小于3
原因在于isIterateeCall
的判断,对于以下情况isIterateeCall
判断会返回ture
function a () {} a['a'] = 1 console.log(isIterateeCall(1, 'a' , a)) // true
但是这种情况下,
sources
的参数只有 3 个也可以满足第二步的判断,如果不做< 3
的判断来处理customizer
,就可以导致错误的结果所以,如果刚好
sources
的长度为 3,同时 这三个参数作为isIterateeCall
的判断,也要判断 是否满足了 第二步 中判断customizer
的条件,如果这两条都满足了,那就要判断length
是否< 3
, 如果小于3,那就证明,最后传入的函数 不是自定义函数,而是作为某个迭代的原始对象,不符合customizer
的条件,要将customizer
置为undefined
最后一步就相对简单了,
while
循环,然后取到每一个传入的真实对象参数,如果值存在,调用assigner
函数进行合并即可
# Remark
- 逗号操作符 MDN (opens new window) 对它的每个操作数求值(从左到右),并返回最后一个操作数的值。
- Object.assign() MDN (opens new window) 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
# Example
const assigner = (object, source) => {
if (Array.isArray(object) && Array.isArray(source)) {
for (let k of source) {
object.push(k)
}
} else {
for (const k in source) {
object[k] = source[k]
}
}
}
const func = createAssigner(assigner)
const a = [1,2,3,4,5]
let b = [7]
func(b, a)
console.log(b) // [ 7, 1, 2, 3, 4, 5 ]