Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the 'this' keyword be used inside a RequireJS module?

I defined a RequireJS module (see below). It is a collection of functions that are needed on every page on a site.

One of the functions in the returned object, initTwitter(), needs to call another function, shortenURL(), in the same object. I am getting a console error that reads, "TypeError: this.shortenURL is not a function."

All of these functions were originally in a regular Javascript singleton object, and the code ran fine. I'm new to RequireJS, so I'm not sure if the this keyword works differently in RequireJS, or I am simply doing something wrong.

EDIT: I also tried removing 'this' but I get a "ReferenceError: shortenURL is not defined" message instead.

define(['config', 'jquery'], function(config, $) {
    /* Site-wide functions go here */

    return {
        doesObjPropertyExist: function(obj, prop) {
            var parts = prop.split('.');
                for(var i = 0, l = parts.length; i < l; i++) {
                    var part = parts[i];
                    if(obj !== null && typeof obj === "object" && part in obj) {
                        obj = obj[part];
                    }
                    else {
                        return false;
                    }
                }
            return true;
        },
        /** 
         * Shorten URL via bit.ly
         * Requires a callback function, so that once getJSON is absolutely finished, we can continue with the function
         */
        shortenURL: function(longURL, callback, settingsOverride) {
            var defaults = {
                login: '@bitly.username@',
                apiKey: '@bitly.key@'
            };

            $.extend({}, defaults, settingsOverride);
            var bitly_service_url = "http://api.bit.ly/v3/shorten?" + "&login=" + defaults.login + "&apiKey=" + defaults.apiKey + "&longUrl=" + longURL + "&format=json&callback=?";
            $.getJSON(bitly_service_url, function(results){
                if (results.status_txt === "OK") {
                    callback(results.data["url"]);
                }
                else {
                    callback(longURL);
                }
            });
        },
        initTwitter: function() {
            var bitly_obj = '',
                $bitly_link = '',
                twitterShortenUrl = function(obj, $elem) {
                    this.shortenURL(obj.url, function(url) {
                        $elem.attr('href', 'http://twitter.com/home?status=' + 'From @Real_Simple ' + arg_obj.headline + ' ' +  shortened_url);
                    });
                };

                $bitly_link = $('#share-twitter');

                /* On document.ready, only call bitly service and change the link href if the twitter link exists; cannot use page_context, 
                 * because we are looking for a specific element ID that is only placed when Twitter is needed. 
                 */
                if ($bitly_link.length) {
                    bitly_obj = config.page_context.bitly;

                    twitterShortenUrl(bitly_obj, $bitly_link);
                }
        },
        initFullSiteLink: function() {
                var canonical_url = $('link[rel="canonical"]').attr('href'),
                    content_type = config.markup_id.content;

                    if (content_type == 'search') {
                        $('#fullsite').attr('href', canonical_url + location.search + '&nomobile=1');
                    } else {
                        $('#fullsite').attr('href', canonical_url + '?nomobile=1');
                    }
        },
        initNav: function() {
            /* Global navigation */
            /* Make Channels button open and close the global flyout navigation */
            $('#channels-toggle').bind('click', function() {
                if ($('#channels').hasClass('is-open')) {
                    $('#channels').removeClass('is-open');
                } else {
                    $('#channels').addClass('is-open');
                }

                return false;
            });

            /* Touch the close button in the global flyout navigation to close it */ 
            $('#channels .close').bind('click', function() {
                $('#channels').removeClass('is-open');

                return false;
            });
        },
        omniture: {
            mobilePageTrack: function (desc) {
                /* Global function in rsmobile.js */
                omniPg(desc);
            }
        },
        init: function() {
           this.initNav();
           this.initTwitter();
           this.initFullSiteLink();
        }
    }   /* End return object */
});

EDIT 2: It turns out that 'this' in my code was inside another function, so its scope changed to window. I need to preserve the scope of this prior to entering the function.

initTwitter: function() {
            var bitly_obj = '',
                $bitly_link = '',
                self = this;    /* Preserve scope of this */

            var twitterShortenUrl = function(obj, $elem) {
                    self.shortenURL(obj.url, function(url) {
                        $elem.attr('href', 'http://twitter.com/home?status=' + 'From @Real_Simple ' + obj.headline + ' ' + url);
                    });
                };
/* rest of code .... */

Otherwise, Paul's answer highlights that I can return anything and add initialization too.

like image 221
Stephen Avatar asked Feb 01 '13 16:02

Stephen


People also ask

What is a RequireJS module?

RequireJS is a JavaScript file and module loader. It improves perceived page load times because it allows JavaScript to load in the background. In particular, it enables asynchronous JavaScript loading.

What is RequireJS used for?

RequireJS is a JavaScript library and file loader which manages the dependencies between JavaScript files and in modular programming. It also helps to improve the speed and quality of the code.

What is module keyword in JavaScript?

module keyword refers to the object representing the current module. The module object has a key named exports . module. exports is another object which is used for defining what can be exported by a module and can be made available to other modules.

What is define in RequireJS?

The define function is a function created by RequireJS, and it takes two parameters: An array of paths to JavaScript files needed as dependencies. The function to execute after RequireJS ensures those dependencies have been loaded.


1 Answers

No, because Require.js doesn't do any magic binding for this. You should rewrite your module to be an instance object:

define(['config', 'jquery'], function(config, $) {
    function obj() {
        this.initNav();
        this.initTwitter();
        this.initFullSiteLink();
    }
    obj.prototype = {
        doesObjPropertyExist: function (/*...*/) { /*...*/ },
        shortenURL: function (/*...*/) { /*...*/ },
        initTwitter: function (/*...*/) { /*...*/ },
        initFullSiteLink: function (/*...*/) { /*...*/ },
        initNav: function (/*...*/) { /*...*/ },
        omniture: function (/*...*/) { /*...*/ }
    };
    return obj;
});

require(['that_main_module'], function (obj) {
    var newObj = new obj();
});
like image 156
Paul Armstrong Avatar answered Oct 14 '22 03:10

Paul Armstrong