误解
我在buildObjKey 之前定义buildObj 并在尚未定义时调用buildObjKey...这是不好的做法,但如果我在buildObjKey 之后移动buildObj 的定义,我将在之前调用buildObj它被定义了......
这是不真实的。 buildObj 是一个函数,在你定义buildObjKey 之前它不会被调用。 buildObj 包含对 buildObjKey 的引用这一事实并不意味着它会尝试立即调用它。为了具体说明这一点,让我们看一个下面的简化示例。
注意isEven 和isOdd 如何不产生输出直到其中一个函数被实际调用 -
function isEven (n)
{ console.log("isEven", n)
if (n == 0)
return true
else
return isOdd(n - 1) // <- calls isOdd
}
function isOdd (n)
{ console.log("isOdd", n)
if (n == 0)
return false
else
return isEven(n - 1) // <- calls isEven
}
console.log("first line of output")
console.log("result", isOdd(3))
first line of output
isOdd 3
isEven 2
isOdd 1
isEven 0
result true
可能且强大
这可以在两个函数之间进行递归调用而没有这个麻烦吗?
这是一种非常强大的技术,称为mutual recursion。相互递归是处理递归树(如程序中的深度嵌套对象)的绝佳方式。你有很好的直觉以这种方式处理它们。有关实际示例和说明,请参阅this Q&A。
相关
巧合的是,我在this Q&A 中编写了一个通用的对象差异函数。这展示了泛型函数和可重用代码的优势。使用您问题中的test1 和test2 输入,我们可以计算出精确的差异,而无需对原始代码进行任何修改-
const test1 = {
"common": {
"setting1": "Value 1",
"setting2": 200,
"setting3": true,
"setting6": {
"key": "value",
"doge": {
"wow": ""
}
}
},
"group1": {
"baz": "bas",
"foo": "bar",
"nest": {
"key": "value"
}
},
"group2": {
"abc": 12345,
"deep": {
"id": 45
}
}
}
const test2 = {
"common": {
"follow": false,
"setting1": "Value 1",
"setting3": null,
"setting4": "blah blah",
"setting5": {
"key5": "value5"
},
"setting6": {
"key": "value",
"ops": "vops",
"doge": {
"wow": "so much"
}
}
},
"group1": {
"foo": "bar",
"baz": "bars",
"nest": "str"
},
"group3": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
console.log(diff(test1, test2))
展开下面的sn-p,在自己的浏览器中验证diff的结果-
const isObject = x =>
Object(x) === x
const isArray =
Array.isArray
const mut = (o, [ k, v ]) =>
(o[k] = v, o)
const diff1 = (left = {}, right = {}, rel = "left") =>
Object
.entries(left)
.map
( ([ k, v ]) =>
isObject(v) && isObject(right[k])
? [ k, diff1(v, right[k], rel) ]
: right[k] !== v
? [ k, { [rel]: v } ]
: [ k, {} ]
)
.filter
( ([ _, v ]) =>
Object.keys(v).length !== 0
)
.reduce
( mut
, isArray(left) && isArray(right) ? [] : {}
)
const merge = (left = {}, right = {}) =>
Object
.entries(right)
.map
( ([ k, v ]) =>
isObject(v) && isObject(left [k])
? [ k, merge(left [k], v) ]
: [ k, v ]
)
.reduce(mut, left)
const diff = (x = {}, y = {}, rx = "left", ry = "right") =>
merge
( diff1(x, y, rx)
, diff1(y, x, ry)
)
const test1 = {
"common": {
"setting1": "Value 1",
"setting2": 200,
"setting3": true,
"setting6": {
"key": "value",
"doge": {
"wow": ""
}
}
},
"group1": {
"baz": "bas",
"foo": "bar",
"nest": {
"key": "value"
}
},
"group2": {
"abc": 12345,
"deep": {
"id": 45
}
}
}
const test2 = {
"common": {
"follow": false,
"setting1": "Value 1",
"setting3": null,
"setting4": "blah blah",
"setting5": {
"key5": "value5"
},
"setting6": {
"key": "value",
"ops": "vops",
"doge": {
"wow": "so much"
}
}
},
"group1": {
"foo": "bar",
"baz": "bars",
"nest": "str"
},
"group3": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
console.log(diff(test1, test2))
{
"common": {
"setting2": {
"left": 200
},
"setting3": {
"left": true,
"right": null
},
"setting6": {
"doge": {
"wow": {
"left": "",
"right": "so much"
}
},
"ops": {
"right": "vops"
}
},
"follow": {
"right": false
},
"setting4": {
"right": "blah blah"
},
"setting5": {
"right": {
"key5": "value5"
}
}
},
"group1": {
"baz": {
"left": "bas",
"right": "bars"
},
"nest": {
"left": {
"key": "value"
},
"right": "str"
}
},
"group2": {
"left": {
"abc": 12345,
"deep": {
"id": 45
}
}
},
"group3": {
"right": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
}