Big Arguments

August 3, 2013 by Christopher Walker

I don’t like fights

But we’re not going to discuss fights today. We are going to discuss arguments, and more specifically, JavaScript function arguments. Now we all know that functions CAN take arguments (and we also know that they don’t necessarily need to).

The cool thing about JavaScript functions is that while they are functions, they are also objects. This is a very significant misunderstanding of the language. In fact, besides functions, everything else that has a type in JavaScript is an object. Strings, numbers, regular expression literals, arrays (and so on and so forth) are all objects. Well that’s pretty interesting LIAR! Prove it!

Ok, I will. Take this example:

    var str = "Hello";
    var num = 4;
    var arr = [str, num];
    var fn  = function() {return "foo"};

    console.log(typeof str); //String
    console.log(typeof num); //Number
    console.log(typeof arr); //Object
    console.log(typeof fn);  //Function

Look at the last three highlighted lines. I show you their type in order. Now take the following code and you’ll see how they are all actually Objects in JavaScript.

    Object.prototype.proveIt = function() {
        console.log("I told you so");
    }; // People argue if it's bad to prototype natives
       // I disagree

    str.proveIt(); //I told you so
    num.proveIt(); //I told you so
    arr.proveIt(); //I told you so
    fn.proveIt();  //I told you so

See what happens? I modified the prototype of JavaScript’s native Object type, and it was reflected in all the other types. This proves that they are all Objects in JavaScript.

In a question (which has been deleted because it was an awful question) on StackOverflow.com, a user asked what the difference between all these different things were, like Objects, Functions, Prototypes, etc. I think my comment back thorougly summed it up:

A function is an object, objects aren’t necessarily functions, functions have prototypes and are an object but don’t necessarily have functions. Functions return value…but only if they want to. Objects have properties…or they don’t…that’s an empty object. Common objects share a common prototype object…which is an object.

Why is all this significant?

Functions are objects, which means functions can become object instances. In fact, in JavaScript, that’s how you create objects and then make them into an instance, you use functions.

So for example, say you want to make a Person object. This object will represent an instance of a person. Think of all the qualities of a person or the things that could be used to describe said person. These would be the properties of that object.

  • First and last name
  • Maybe a middle initial
  • Height
  • Weight
  • Gender
  • Hair color
  • Favorite ice cream flavors

We know how we want to describe our person, now how do we reflect this in code. This is where I come to the subject I’d like to discuss.

The Big Argument Debacle

A.K.A. The B.A.D.

I have seen code like this before, and it boggles my mind how people maintain it:

    var Person = function(first, middle_initial, last, height, weight, gender, hair_color, ice_cream_favorites) {
        //do person stuff
    }
    var person1 = new Person(/* omg! */);

How do you live like that

That argument list for that function is gigantic and also a gigantic mess. This could lead to problems like devs forgetting which order to add arguments when creating a new instance or in instances where one of the properties doesn’t necessarily need to be added (nobody likes looking at the word null). Fortunately, I have come up with a pretty good solution (I think). Defaults!

In PHP, declaring defaults for your function is easy. A function argument default is simply declared in the function arguments list i.e.:

    //param2 has a default value and will maintain that value unless it is added on call
    function some_func(param1, param2 = "This is the default") {
        //do stuff
    }

In Java, you can overload methods, i.e.:

    //1 arguments
    public void someMethod(String name) {
        //do stuff
    }

    //Overloaded with 2 arguments
    public void someMethod(int id, String name) {
        //do stuff
    }

In JavaScript, we have prototype!

My proposal

My proposal is simple. Create a BaseObject that all objects can extend within their prototype. This BaseObject will have several methods, but a key one will be a setDefaults method that runs through a list given by the child object. The list will be a literal object. It would look something like this:

    var BaseObject = function (initObj) {
        this.initObj = initObj;

        //Set the properties of the initObj
        //as properties of 'this'
        this.init = function (objList) {
            for (var item in objList) {
                if (objList.hasOwnProperty(item)) {
                    this[item] = objList[item];
                }
            }
        };

        //Use this given defaults object to set everything else
        this.setDefaults = function (defaults) {
            this.defaults = defaults;
            this.prototype.init.call(this, this.prototype.initObj);
            for (var item in defaults) {
                if (defaults.hasOwnProperty(item) && !this.hasOwnProperty(item)) {
                    this[item] = defaults[item]
                }
            }
        };
    };

I have highlighted the pertinent lines:

  • Line 2 is obvious but important. The this.initObj object will be passed from a newly created object as the new object instance’s member values. Setting this in the base object allows us to run through it, setting it’s members, and then it will keep that information within the prototype. This could be useful for seeing how the object was constructed.
  • Line 14 has a similar purpose to line 2 in that it can be kept for historical reasons down the line, i.e. if you wanted to reset the defaults of an object, you wouldn’t have to create a new object with the same variable name in it’s place. You could just run a simple function that rolls through the members called resetToDefault(), but that’s beyond our scope right now.

Now that we have our BaseObject set up, let’s do something with it, like making a Person object.

    var Person = function (initObj) {
        // Set the prototype to our BaseObject
        this.prototype = new BaseObject(initObj);

        // Set the defaults in an object
        var defaults = {
            name: {
                first: "John",
                middle_initial: null,
                last: "Smith"
            },
            height: 175,
            weight: 70,
            gender: "m",
            hair_color: "brown",
            ice_cream_favorites: []
        };
        this.prototype.setDefaults.call(this, defaults);
    };
  • Line 3: Here you see the usefulness of JavaScript’s prototypal inheritance. We assign the prototype of our Person object to a new base object instance to which we pass our initializing literal object. Then our base object will do all the work setting the defaults for the Person!
  • Line 18: Utilizing the methods given to us from our base object prototype, we call the setDefaults() method and let the base object go to town setting members.

Now let’s make this Person object a real boy (or girl)

And my nose isn’t even growing!

    var JaneDoe = new Person({
        name: {
            first: "Jane",
            middle_initial: "P",
            last: "Doe"
        },
        height: 155,
        weight: 52,
        ice_cream_favorites: ["Strawberry", "Vanilla"]
    });

Now you notice we excluded a single member of the defaults object, and that is hair color, but since we are instantiating a new person object that has predefined defaults and has a prototype inherited base object to do something with those defaults, the hair_color member is still filled with a value of "brown".

She has brown hair color

As you can see, our BaseObject has done it’s job and we can rest easy at night knowing that the size of arguments in our functions will be decreased to one.

TL;DR

Just start at the code

Privacy Policy

© 2017 | Powered by Hugo and lots of motivation.