3

I am doing a small JavaScript module with an object of defaults options, it looks like this:

 var myModule = function() {

  // Define option defaults
  var defaults = {
    foo: 'bar',
    fooObject: {
      option1: [],
      option2:true,
      option3: false,
    }
  }

  // Create options by extending defaults with the passed in arugments
  if (arguments[0] && typeof arguments[0] === "object") {
    this.options = extendDefaults(defaults, arguments[0]);
  }
}

so when I call my module like that

var moduleInstance = new myModule({
  foo: 'some value',
  fooObject: {
    option1: [1, 2, 3]        
  }
});

moduleInstance.options.foo; //will retrurn an array [1, 2, 3]

but

moduleInstance.options.fooObject; //will only contain option1

I understand why as option2 and option3 are not defined when creating the object, but i can't wrap my head around how to solve this. All the snippets that I found where using jQuery on another framework.

edit:

sorry the extendDefault() was missing here it is:

    function extendDefaults(source, properties) {
  var property;
  for (property in properties) {
    if (properties.hasOwnProperty(property)) {
      source[property] = properties[property];
    }
  }
  return source;
}

Edit: possible work around

i ended up doing it like this http://jsfiddle.net/optionsit/sgmme5dy/

in the loop that check for hasOwnProperty

I put that if statement if(typeof properties[property] === 'object' && typeof properties[property].nodeType === 'undefined' )

so I can check if the value is an object but not a DOM element (as I am passing some DOM element in the top level values as well)

if it is a JavaScript object I iterate trough it's children to see if they are set in the arguments and replace them only if they are.

It may not be very pretty but it works for my use case, please feel free to comment if you have a better option I will accept a prettier answer.

Thanks for your help

2
  • Could you show us extendDefaults? Commented Aug 11, 2015 at 9:24
  • I guess the problem is inside extendDefaults or the if check? Commented Aug 11, 2015 at 9:25

2 Answers 2

1

You simply need to call your extendDefaults recursively:

function extendDefaults(source, properties) {
    var property;

    for (property in properties) {
        if (properties.hasOwnProperty(property)) {
            if (typeof properties[property] === 'object' && typeof properties[property].nodeType === 'undefined') {
                extendDefaults(source[property], properties[property]);
            } else {
                source[property] = properties[property];        
            }
        }
    }

    return source;
}

Here's an updated fiddle: http://jsfiddle.net/sgmme5dy/3/

Keep in mind that this function will break on null values and Date properties, because: typeof null === 'object' and typeof new Date === 'object'.

To handle those, just add this to the if:

if (typeof properties[property] === 'object' && properties[property] !== null && !(properties[property] instanceof Date) && typeof properties[property].nodeType === 'undefined')

Edit: totally forgot about Arrays, they are typeof object too. In the end I think it would be best to just rely on one of the many existing extend functions out there. For example if you use jQuery you can use $.extend, or of you use lodash/underscorejs you can use _.assign. If you don't use or don't want to use any of these libraries, then there are small standalone libraries also, like this one: node-deep-extend

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! it works really great i taught of calling it recursively but without success. actually Arrays does not seem to be a problem. I have nothing against jQuery or other framework but as i have other dependencies here it's not an option, also thanks for the link to node-deep-extend i'll be sure to check that out
With this solution, arrays may produce unexpected results, for example here: jsfiddle.net/sgmme5dy/6 What result of fooObject.options1 would you expect? Normally, options1 should be [9000], instead of [9000, 2, 3]
oh I see, for now it seems like adding && Array.isArray(properties[property]) is doing the trick but I will concidere moving to something more robust
1

Your code replaces the whole fooObject that's probably not your intention. There are multiple ways to solve this depending on your requirements. You could for example add an extra method in case you want to "Add" new things to the fooObject instead of using the "constructor".

 function extendDefaults(source, properties) {
   var property;
   for (property in properties) {
       if (properties.hasOwnProperty(property)) {
           source[property] = properties[property];
       }
   }
   return source;
 }


var myModule = function () {

   // Define option defaults
   var defaults = {
       foo: 'bar',
       fooObject: {
           option1: [],
           option2: true,
           option3: false,
       }
   }

   this.options = extendDefaults({}, defaults);

   // Create options by extending defaults with the passed in arugments
   if (arguments[0] && typeof arguments[0] === "object") {
       this.options = extendDefaults(this.options, arguments[0]);
   }
}

var moduleInstance = new myModule({
   foo: 'some value',
});

moduleInstance.options.fooObject.option1 = [1, 2, 3];


console.log(moduleInstance.options.fooObject);

console.log(moduleInstance.options.foo);

https://jsfiddle.net/w5ut28es/

4 Comments

Yeah I am switching to this.defaults = { foo:'bar', 'fooObject': { ... } }; but if i want to merge my default options wit the options the user enter in the object, I will still need the extendDefaults or am I missing something?
It merges in the top level but not on a child level. The childs just get replaced in the code you provided.
Thanks for the jsfiddle, it crossed my edit, I will check that out
I think this could still be better. I would see what I did there just as a workaround for "merging" the children. What you want is probably a function which checks if the property is already existing in the source and instead of replacing it should only add the additional properties.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.