【问题标题】:How recursion takes place in this code snippet?在这个代码片段中递归是如何发生的?
【发布时间】:2016-08-21 04:42:15
【问题描述】:

查看调用 Stack 和 Watch,现在当我按下 F10 时,“Watch”中的行值变为“object Object”,因此一切都发生了,我了解了直到此时的流程,然后发生了什么不清楚。现在在病房里,为什么即使我在第 8 行提供断点,代码也不会在第 8 行停止

这是stackoverflow的问题

我正在尝试理解这段代码 sn-p

function assign(obj, prop, value) {
    if (typeof prop === "string")
        prop = prop.split(".");

    if (prop.length > 1) {
        var e = prop.shift();
        assign(obj[e] =
                 Object.prototype.toString.call(obj[e]) === "[object Object]"
                 ? obj[e]
                 : {},
               prop,
               value);
    } else
        obj[prop[0]] = value;
}

var obj = {},
    propName = "foo.bar.foobar";

assign(obj, propName, "Value");

递归是如何发生的,因为函数没有返回任何东西? 内部调用中的参数值如何随着最顶部调用堆栈(分配函数)中的函数而改变?

感谢大家的回答,但我的确切问题是它是如何在这一特定行(第 8 行)发生的。

【问题讨论】:

  • javascript 中的对象是通过引用而不是值传递的。这意味着当您将对象引用作为函数的参数传递时,函数的参数仍然引用同一个对象。
  • 使用调试器遍历代码可能很有用。
  • 已经完成了调试,你们能解释一下吗,这个 - Object.prototype.toString.call(obj[e]) - value 第一次变成“object Object”并且为什么即使我在那里应用了断点,当最高调用堆栈中的函数开始返回时,代码也不会在函数的内部调用处停止?
  • Object.prototype.toString.call(obj[e]) === "[object Object]" 不应在上述 sn-p 的第一次运行中返回 true。我认为它也可以检查为typeof obj[e] === "object" 另一方面,即使不需要,如果这些函数返回结果对象以用于链接目的会更好。

标签: javascript object recursion


【解决方案1】:

Object.prototype.toString.call(obj[e]) === "[object Object]" 永远不会是真的。总是"[object Undefined]"

  1. 开头:

    obj:: {}
    prop:: 'foo.bar.foobar'
    
  2. 在第一次调用内部函数之前 (if):

    obj:: {foo: {}}
    prop:: ['bar', 'foobar']
    
  3. 在第二次调用内部函数之前 (if):

    obj:: {foo: {bar: {}}}
    prop:: ['foobar']
    
  4. 最后执行时,prop.length 为 1(else - 不再递归):

    obj:: {foo: {bar: {foobar: 'Value'}}}
    prop:: []
    

Javascript 中的对象总是作为引用传递,参考@Tiny Giant 的评论。

【讨论】:

  • 如果我添加 obj 来观察并观察它的值,它会像这样变化 ---------------- obj = {} 然后 {foobar: ' Value'} 然后 {bar: {foobar: 'Value'}} 然后 {foo: {bar: {foobar: 'Value'}}} 。 :( 我不明白它如何从 {foobar: 'Value'} 变为 {bar: {foobar: 'Value'}} 等等。
  • 没错。实际上,当递归调用返回时,手表中的 obj 也会更新!无需在手表中跟踪,只需放置 console.log() 并在需要的地方打印 obj。
【解决方案2】:

Object.prototype.toString.call(obj[e]) === "[object Object]" 只是说“(如果)这个对象确实存在......”的一种非常丑陋的方式。我不会详细解释它是如何工作的,因为它不是一个很好的例子,你可能不明白。

代码想要的是能够添加/编辑对象属性。例如:

var person = {name: 'Liana', hair: {color:'red', style:'long'}};
assing(person, 'name', 'George');

...等等。但是如果你想编辑一个嵌套属性,比如头发颜色呢?

assign(person, 'hair.color', 'brown');

基本上,assign 函数被调用了两次:

  • assign(person, 'hair.color', ...)
  • 这检测到我们正在编辑 hair 内的嵌套属性 (color),并且 它确保hair 确实存在于person 中。
  • 如果没有,它会创建person.hair(这就是丑行的意义所在)
  • 然后调用assing(person.hair, 'color', 'brown');

我个人会这样写函数:

function assign(object, prop, value) {
    prop = prop.split('.');

    if(prop.length > 1) {
        var first = prop.shift();
        prop = prop.join('.');
        if(typeof object[first] != 'object')
            object[first] = {};
        assign(object[first], prop, value);
    } else {
        object[prop] = value;
    }
}

【讨论】:

  • 您的代码 sn-p 很棒,我希望我能接受这个答案,但我的问题是关于我编写的代码的工作流程。
  • @Promote Nabid 回答比我解释得更好。我编写该代码是因为我认为它可以帮助您理解原始代码的作用。 (其实是一样的,只是写的比较晦涩)
【解决方案3】:

递归是如何发生的,因为函数没有返回任何东西 ?

返回值不是递归发生的必要条件。这里,条件prop.length > 1 将在将值设置为所需属性后停止递归

内部调用中的参数值如何更改为 最顶部调用堆栈(分配函数)中的函数完成了吗?

我不确定你在这里问什么。我想你是在问这条线

assign(obj[e] =
                 Object.prototype.toString.call(obj[e]) === "[object Object]"
                 ? obj[e]
                 : {},
               prop,
               value);

此代码检查obj 是否为对象。如果obj 不是对象,则在递归调用本身中将其更改为对象。

【讨论】:

    猜你喜欢
    • 2015-01-21
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 2021-11-16
    相关资源
    最近更新 更多