Archives

Archives / 2011 / June
  • JavaScript: this

    JavaScript is a language steeped in juxtaposition.  It was made to “look like Java,” yet is dynamic and classless.  From this origin, we get the new operator and the this keyword.  You are probably used to this referring to the current instance of a class, so what could it mean in a language without classes?

    In JavaScript, this refers to the object off of which a function is referenced when it is invoked (unless it is invoked via call or apply). What this means is that this is not bound to your function, and can change depending on how your function is invoked. It also means that this changes when declaring a function inside another function (i.e. each function has its own this), such as when writing a callback. Let's see some of this in action:

    var obj = {
        count: 0,
        increment: function () {
            this.count += 1;
        },
        logAfterTimeout = function () {
            setTimeout(function () {
                console.log(this.count);
            }, 1);
        }
    };
    obj.increment();
    console.log(obj.count); // 1
    var increment = obj.increment;
    window.count = 'global count value: ';
    increment();
    console.log(obj.count); // 1
    console.log(window.count); // global count value: 1
    var newObj = {count:50};
    increment.call(newObj);
    console.log(newObj.count); // 51
    obj.logAfterTimeout();// global count value: 1
    obj.logAfterTimeout = function () {
        var proxiedFunction = $.proxy(function () {
                console.log(this.count);
            }, this);
        setTimeout(proxiedFunction, 1);
    };
    obj.logAfterTimeout(); // 1
    obj.logAfterTimeout = function () {
        var that = this;
        setTimeout(function () {
            console.log(that.count);
        }, 1);
    };
    obj.logAfterTimeout(); // 1

    The last couple of examples here demonstrate some methods for making sure you get the values you expect.  The first time logAfterTimeout is redefined, we use jQuery.proxy to create a new function which has its this permanently set to the passed in value (in this case, the current this).  The second time logAfterTimeout is redefined, we save the value of this in a variable (named that in this case, also often named self) and use the new variable in place of this.

    Now, all of this is to clarify what’s going on when you use this.  However, it’s pretty easy to avoid using this altogether in your code (especially in the way I’ve demonstrated above).  Instead of using this.count all over the place, it would have been much easier if I’d made count a variable instead of a property, and then I wouldn’t have to use this to refer to it. 

    var obj = (function () {
        var count = 0;
        
        return {
            increment: function () {
                count += 1;
            },
            logAfterTimeout = function () {
                setTimeout(function () {
                    console.log(count);
                }, 1);
            },
            getCount: function () { return count; }
        };
    }());

    If you’re writing your code in this way, the main place you’ll run into issues with this is when handling DOM events (where this is the element on which the event occurred).  In that case, just be careful when using a callback within that event handler, that you’re not expecting this to still refer to the element (and use proxy or that/self if you need to refer to it).

    Finally, as demonstrated in the example, you can use call or apply on a function to set its this value.  This isn’t often needed, but you may also want to know that you can use apply to pass in an array of arguments to a function (e.g. console.log.apply(console, [1, 2, 3, 4])).

  • JavaScript: Global Variables

    One of the biggest issues that you can run into with writing JavaScript is abusing the shared global namespace, and overwriting the variables of an unrelated script.  Everything that you declare in JavaScript is available to every other script on the page, unless you wrap the declaration in a function.  Also, if you use a variable without declaring it (even within a function), it will be declared for you in the global scope.

    There are a couple of remedies we have against creating a global soup of conflicting variables.  Knowing is half the battle, so step one is just keeping an eye out.  Recognize when you’re creating a function or variable, and make sure that it’s not a global.  The main technique for this is to declare everything inside a function.  If you’re writing jQuery scripts, most of your script is probably being written inside the callback passed to jQuery(document).ready(), so you’re mostly safe already.  If you’re not using a function already, you can wrap your code in an immediately invoked function expression.

    Step two is to catch when you miss declaring a variable.  There are two ways to do this.  The first is to turn on strict mode, so that browsers which support strict mode will throw an error when you try to access an undefined variable (instead of defining it as a global).  In addition, you should be running your code through a linter (JSLint or JSHint) to catch issues like this.

    So, now that you know how not to expose global variable, what about if you need to?  If you need to write code in one script and reference it from another, you’ll need to expose a reference to that code somehow, right?

    In this case, you should still probably be using all of the above techniques to keep the scope clean, but then explicitly expose anything that you need to.  Again, if you’re using jQuery, this is fairly natural.  Your script will take a reference to the global jQuery function, and you can add your script as a plugin to jQuery.fn.  If you’re not exposing a jQuery plugin, you can take a reference to the global window object, and add your object explicitly.  As an example:

    (function (window, $) {
        “use strict”; // turn on strict mode
        var x = 10; // x will not be accessible outside of this function
        y = x * 20; // y will be declared as a global variable, unless the browser supports strict mode
        $.fn.myPlugin = function (args) { ... }; // expose your plugin to the rest of the page
        window.textbox2_validate = function (sender, args) { ... }; // expose a standard function
    }(this, jQuery)) // immediately invoke this function expression, passing in this (a reference to the global scope) and jQuery
    
  • JavaScript Functions

    When coming from a strongly-typed, object-oriented background, there may be some surprising elements when looking at how functions are used in JavaScript.

    Functions in JavaScript don’t have any real concept of a signature, like they would in .NET.  All functions return a value (which will be the value undefined if nothing is explicitly returned), so there are no void functions, and, while you can define named parameters for a function, callers of the function can provide more or less parameters than specified.  If less parameters are provided, the other parameters will be undefined, if more are provided, they are accessible via an implicit array-like variable named arguments.  This also means that it’s not possible to have function overloads which have the same name and differ just by signature.

    There are also two different ways to create a function.  The one that looks more like the object-oriented style is a function statement.  This is a statement that starts with function, then gives the function name, parameter list, and body.  One special characteristic of function statements is that they are defined throughout the entire scope of their containing function (i.e. they can be used before they are defined).  They are said to be hoisted to the top of the containing function.

    The other option is a function expression.  This can look just like a function statement, but doesn’t require a function name, and is a value that can be assigned to a variable, passed to another function, or invoked.  Let’s see some examples:

    var myFunc = function (x) {
        if (typeof x === 'undefined') {
            return 'no args';
        } else if (typeof x === 'function') {
            return x();
        } else if (arguments.length === 1) {
            return 'x was ' + x;
        } else {
            return arguments.length - 1 + ' extra arguments provided';
        }
    },
        result1 = myFunc(),
        result2 = myFunc(function (y) { console.log(y); }),
        result3 = myFunc(statement),
        result4 = myFunc(1),
        result5 = myFunc({}, true, 'text');
    console.log(result1, result2, result3, result4, result5);
    function statement(x, y, z) {
        console.log(x, y, z);
    }

    So, we start here by defining a variable myFunc, whose value is a function expression.  The function has one parameter, x, and returns a string value indicating what was passed in.  For result1, we call myFunc without any arguments, and see that x is undefined.  For result2 and result3, we call myFunc with a function argument (for result2 it’s a function expression, for result3 it’s a reference to a function defined via a function statement), call that function, and return the result.  Because both functions we pass in don’t return anything, the result is undefined.  Note that in result3, we were able to use statement before it was defined.  For result4, we call myFunc with one argument, and refer to the value via the parameter x.  For result5, we call myFunc with more than one argument, and refer to the arguments collection.

  • Why isn’t My Scheduled Task Executing?

    A quick diversion from the JavaScript Common Difficulties and Misconceptions series (I have another post queued up, just need to write the sample code), for an issue I just ran into.

    Created a scheduled task for DNN, but it’s sitting in the scheduler queue, just getting more and more overdue.  Why won’t my task run?

    Somehow I missed the constructor from the tasks I copied.  Your task needs a constructor that takes a ScheduleHistoryItem to run via the scheduler.