• To Arrow Function or not to Arrow Function

    Published by on February 8th, 2013 12:35 am under Emerging Technology, JavaScript, TypeScript

    No Comments

    If you have used TypeScript you are probably aware of the compiled outcome of an Arrow Function (section 4.9.2 of the TypeScript Language Specification). The idea behind using () => {…}; instead of function(){…}; is to have access to the object represented by this in the outer scope, which is what you would expect when using a C# lambda. Of course this feature is particulalry useful in JavaScript because this holds a reference to the object to which a function was applied or called, and more often than not the this inside the function’s execution context ends up being different from the one in the outer execution context.

    When using an Arrow Function, TypeScript automatically assigns this to a separate variable in the outer execution context (called _this), which can be accessed by the function that results of the compilation of the Arrow Function through its closure. As the previous sentence was probably not the simplest one to digest, take the following TypeScript code snippet as an example:

    class MyClass {
        constructor() {
        }
    
        private add(a, b) {
            return a + b;
        }
    
        public partialAdd(a) {
            return (b) => {
                return this.add(a, b);
            }
        }
    
        public partialAdd2(a) {
            return function(b) {
                return this.add(a, b);
            }
        }
    }

    The resulting JavaScript is:

    var MyClass = (function () {
        function MyClass() {
        }
        MyClass.prototype.add = function (a, b) {
            return a + b;
        };
        MyClass.prototype.partialAdd = function (a) {
            var _this = this;
            return function (b) {
                return _this.add(a, b);
            }
        };
        MyClass.prototype.partialAdd2 = function (a) {
            return function (b) {
                return this.add(a, b);
            }
        };
        return MyClass;
    })();

    As you can see, partialAdd stores this in the local _this variable which is accessible from the function that is returned through its closure. On the other hand, partialAdd2 uses this as the variable, but as I mentioned before this might not be “an instance of MyClass” but something completely different instead.

    Complex Cases

    You could (wrongly) deduce that every time you need access to the outer this inside the function you should use an Arrow Function, but what if you need to use both the outer this and the object on which the function was applied? That could be a usual situation in an event handler, which applies the callback functions to the target DOM elements when handling the event (for the sake of the example assume that there is no way to other way to access the DOM element other than this).

    In these cases, it is important to understand what the compiler is doing underneath the hood. The two common approaches that would lead to errors are:

    1. Using an Arrow Function: this would cause TypeScript’s this to shadow the compiled JavaScript’s this, not allowing you to access the DOM elements that was clicked inside the click handler.
    2. Using a function literal: this would cause the outer this to not be accessible, which is unexpected for developers used to C#.

    The Answer

    One possible (and simple) solution to this is the usual workaround (that = this):

    class MyClass {
        constructor() {
        }
    
        private add(a, b) {
            return a + b;
        }
    
        public partialAdd(a) {
            var that = this;
            return function(b) {
                // this is accessible inside the function
                // and points to the object to which the function was applied
                var _this = this;
                // that is accessible inside the function through the closure
                // and points to the object that had partialAdd called
                return that.add(a, b);
            }
        }
    }

    The following figures show that TypeScript’s type inference system confirms the comments from the previous code snippet:

    this can be an instance of any type, as it depends on the object on which the function was called

    image

    that is an instance of MyClass

    image

    Conclusion

    This is not a complex issue, but is one that I’ve seen a couple of times while working with TypeScript, so hopefully if you ever run into it you will be able to solve it in no time.

    Tags: , ,

Archives

Categories