0

I wrote a javascript function to extend an object with a property(s) that follow this sort of notation: "a.b.c". The function works flawlessly (so far) but I couldn't quite understand after looking back over my code why it worked. I'm assuming there is a javascript scope explanation, maybe the for loop? but I was wondering if anyone could explain this to me properly.

var extend = (target, targetProperty, value) => {
  var targetProperties = targetProperty.split(".");

  for (var i = 0, len = targetProperties.length; i < len; i++) {
    var part = targetProperties[i];

    if (i == len - 1) {
      target[part] = value;
      break;
    }

    if (!target[part]) {
      target[part] = {};
    }

    target = target[part];
  }
}

var myObject = {myProperty: "Hello"};

console.log(myObject);

extend(myObject, "a.b.c", "World");

console.log(myObject);

It is pretty straight forward, I understand that "target" is coming in by reference because it is an object, the part that I don't understand is how on the line target = target[part]; the original object being passed in is not completely overwritten by the newly created empty object on this line target[part] = {};.

Thanks!

Ross
  • 273
  • 3
  • 12

2 Answers2

0

The reason is because of break;, which breaks out of the loop and does not run the code after it.

The rest of the code after break is to handle the cases where:

  1. The an intermediate property you are trying to set is falsey. ie. You are trying to set the path foo.bar.baz but foo.bar is undefined, it will assign {} to foo.bar before setting foo.bar.baz.
  2. The intermediate property is truthy, in which case it sets that intermediate property as the new target to process.

This is bad code because what if the intermediate property is not an Object? For example, what if you have this object:

{
  foo: 1
}

And you wanted to set foo.bar.

extend(myObject, 'foo.bar', 'wombat')

It would try to set foo.bar to 'wombat', but foo is 1. How do you set 1.bar? You can't.

kmiyashiro
  • 2,249
  • 14
  • 15
  • You never tried to? `(1).bar = 'wombat';` works flawlessly. – Bergi Feb 25 '16 at 19:52
  • Works as in does what you think it does? Or just doesn't throw an error? – kmiyashiro Feb 25 '16 at 21:11
  • Doesn't throw an error, I mean; of course primitive values don't have properties. But I wouldn't consider the code to be bad because of that - an exception for such a cause could be expected. How would you recommend to improve the code to cope with such a case? – Bergi Feb 25 '16 at 21:19
  • I would expect it to throw as opposed to silently fail so you can handle it. Either that, or overwrite the primitive with an object and just document the behavior. – kmiyashiro Feb 27 '16 at 01:25
0

target only exists in the scope of your function, so changing the value of the target variable only changes it inside the function scope.

You are updating the reference instead of changing the object.

Kyle Trauberman
  • 25,414
  • 13
  • 85
  • 121
  • If that was the case though, then why after executing the function does myObject have all the correct properties added to it? As well as the original property prior to running the function still preserved. – Ross Feb 25 '16 at 19:47
  • @Ross: The function mutates your object's properties. It does not overwrite your object. – Bergi Feb 25 '16 at 19:53
  • @Bergi: I'm quite junior, but I also don't think I asked the question correctly. `target` is a reference to `myObject` in this function. At the end of the function, I assign target to be a new empty object `target = target[part];`. I'm just not sure why myObject did not become that empty object and instead remained in tact. Apologies if I am just not getting it :) – Ross Feb 25 '16 at 19:59
  • `target` is not a reference to `myObject`. `target` is a reference to the same object that `myObject` is a reference to. See also the question I linked as a duplicate above. – Bergi Feb 25 '16 at 20:01
  • Thanks Bergi that definitely explains it! I appreciate the time. – Ross Feb 25 '16 at 20:28