dedupe 函数
正如注释所说,dedupe
函数“比较 latest 和 sealed ,以确保 merge 时,生命周期钩子不会重复”:
function dedupe (latest, extended, sealed) {
// compare latest and sealed to ensure lifecycle hooks won't be duplicated
// between merges
if (Array.isArray(latest)) {
const res = []
sealed = Array.isArray(sealed) ? sealed : [sealed]
extended = Array.isArray(extended) ? extended : [extended]
for (let i = 0; i < latest.length; i++) {
// push original options and not sealed options to exclude duplicated options
if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) {
res.push(latest[i])
}
}
return res
} else {
return latest
}
}
首先,dedupe
函数在 if
语句中调用 Array.isArray
,并把 latest
作为参数,来检测 latest
是否是一个数组。
然后,初始化一个变量 res
,值为空数组。
接着,为了确保 sealed
是数组,通过调用三元运算符重置了 sealed
。如果是数组,sealed
设为 sealed
(例如,dedupe
函数的参数 sealed
)。如果不是数组,就用一个数组包裹下 sealed
。设置 extended
时,也是同 sealed
一样。
sealed = Array.isArray(sealed) ? sealed : [sealed]
extended = Array.isArray(extended) ? extended : [extended]
接下来,dedupe
函数遍历 latest
数组:调用 indexOf
方法来判断 latest
数组中的当前项是否在 extended
数组中,或者不在 sealed
中,如果条件成立,这些选项就会被 push
(追加)到 res
数组里。遍历完后,再返回数组 res
。
for (var i = 0; i < latest.length; i++) {
if (extended.indexOf(latest[i]) >= 0 || sealed.indexOf(latest[i]) < 0) {
res.push(latest[i])
}
}
return res
然而,如果表达式 Array.isArray(latest)
的值为 false
,dedupe
函数返回 latest
。
跳到 resolveModifiedOptions
函数,现在我们理解了通过遍历构造函数的选项属性,判断哪些选项被更改了,如果被更改了,映射一个更改项的对象,并且返回出去。
for (const key in latest) {
if (latest[key] !== sealed[key]) {
if (!modified) modified = {}
modified[key] = dedupe(latest[key], extended[key], sealed[key])
}
}
return modified
最后,我们回到 resolveConstructorOptions
,这个函数在初始化 modifiedOptions
变量时,通过传给 resolveModifiedOptions
函数一个构造函数作为参数设置变量的值。
function resolveConstructorOptions (Ctor) {
// ...
const modifiedOptions = resolveModifiedOptions(Ctor)
// ...
}
因此,modifiedOptions
的值将是一个被更改过的选项 options
的对象。
resolveConstructorOptions
函数然后检测下是否有选项被更改了,如果更改了,调用我们之前讨论过的 extend
函数,所传参数为构造函数的 extendOptions
属性和 modifiedOptions
变量。回一下,extend
函数遍历第二个参数,这里就是 modifiedOptions
,然后把第二个参数的每个键的值设置为第一个参数对应键的值。
再次调用 mergeOptions
,并把返回值设置给 options
和 Ctor.options
。
然后,检查 options.name
是否为 true
。如果为真,把 options.components
上名为 options.name
的属性设置为构造函数 Ctor
。最后,把变量 options
返回。
function resolveConstructorOptions (Ctor) {
// ...
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
return options
}
在这些基础上,最后我们可以返回到 ._init
方法中。._init
方法通过调用 mergeOptions
函数,并传3个参数,设置了一个 $options
属性:
// ...
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
// ...
在以上的讨论中,现在我们理解了传给 mergeOptions
函数的第一个参数:resolveConstructorOptions(vm.constructor)
。在下一章,我们将更详细的讨论 mergeOptions
方法。