4
\$\begingroup\$

I am slowly learning to master JavaScript and particularly the art of self-executing/invoking functions. I have developed a simple JavaScript plugin and I think I have followed the correct standards.

It is pretty basic in what it does, it is to store information on a oData BATCH request, which provides functions to add to the batch request, get the string representation (which can then be used in a HTTP request), and I am expanding on it as we speak.

However, the code is as follows. It was using jQuery but not any more due to resource constraint.

I am generally after comments on what could have been done better. I am doing this in an attempt to reduce the window namespace. Therefore BatchInfo and BatchCacheRequest (which are a bit more specific to my code) are not clogging up the name space, nor do I have to keep an array anywhere do hold the BatchInfo objects.

(function () {
    var info = [],
        b = {
            clear: function () {
                info = [];
            },

            get: function () {
                return info;
            },

            add: function (command, url, data) {
                info.push(new BatchInfo(command, url, data));
                return info;
            },

            toString: function () {
                var batchString = ['--batchfull'];

                batchString.push('Content-Type: multipart/mixed; boundary=batchitems');

                for (var i = 0; i < info.length; i++) {
                    batchString.push('');
                    batchString.push('--batchitems');
                    batchString.push('Content-Type: application/http');
                    batchString.push('Content-Transfer-Encoding: binary');
                    batchString.push('');
                    batchString.push(info[i].Command + ' ' + info[i].URL + ' HTTP/1.1');

                    if (info[i].Command !== 'DELETE') {
                        batchString.push('Content-Type: application/json;type=entry');
                    }

                    batchString.push('');
                    batchString.push(info[i].Data);
                }

                return batchString.join('\r\n') || '';
            }
        };

    function BatchInfo(command, url, data) {
        this.URL = url || '';
        this.Command = command || '';
        this.Data = data || '';
    }

    function BatchCacheRequest(info, description) {
        this.Info = info || '';
        this.Description = description || '';
        this.SubmitDateTime = new Date().toString('yyyy-MM-dd HH:mm');
    }

    window.batch = b;

})();

Usage is:

batch.add('POST', 'http://www.mywebsite.com', '[some data to post]');

Then toString() will return me a string which is formatted like a BATCH request (which I will use later on in the code).

Is there anything I'm doing stupidly bad here?

\$\endgroup\$
  • 1
    \$\begingroup\$ your aside question should be posted on StackOverflow.com. the rest of the question seems to be on topic \$\endgroup\$ – Malachi Nov 20 '13 at 14:28
5
\$\begingroup\$

Not "stupidly bad", but you're invoking .push() far more times than needed. It's a variadic function, so group the items into single invocations.

Additionally, I'd probably put a toString() method on the BatchInfo.prototype that will give back the string representation of the object.

Then put the objects directly into what was the batchString array. That way the .toString() method will be invoked when you call .join().

I've included additional small changes below as well.

(function () {
    var info = [],
        b = {
            clear: function () {
                info.length = 0; // Clear all references
            },
            get: function () {
                return info;
            },
            add: function (command, url, data) {
                info.push(new BatchInfo(command, url, data));
                return info;
            },
            toString: function () {
                // Put the `BatchInfo` objects directly into the Array.
                // Now we don't need the `for` loop.
                return ['--batchfull',
                        'Content-Type: multipart/mixed; boundary=batchitems']
                           .concat(info).join('\r\n'); // Not needed-- || '';
            }
        };
    function BatchInfo(command, url, data) {
        this.URL = url || '';
        this.Command = command || '';
        this.Data = data || '';
    }
    // Handle the `toString()` representation here
    BatchInfo.prototype.toString = function() {
        var parts = ['', '--batchitems',
                     'Content-Type: application/http',
                     'Content-Transfer-Encoding: binary',
                     '',  this.Command + ' ' + this.URL + ' HTTP/1.1'];

        if (this.Command !== 'DELETE') {
            parts.push('Content-Type: application/json;type=entry');
        }
        parts.push('', this.Data);
        return parts.join('\r\n');
    };
    function BatchCacheRequest(info, description) {
        this.Info = info || '';
        this.Description = description || '';
        this.SubmitDateTime = new Date().toString('yyyy-MM-dd HH:mm');
    }
    window.batch = b;
})();
\$\endgroup\$
  • \$\begingroup\$ I really like the use of BatchInfo.prototype.toString as it allows each object to handle displaying itself. Great suggestion! \$\endgroup\$ – Daniel Nov 20 '13 at 17:39
3
\$\begingroup\$

Is there anything I'm doing stupidly bad here?

Nothing I can see. It works and it seems well written. However, you don't appear to be using BatchCacheRequest?

how would I reference functions in b, inside a separate function in b?

Two ways (kind of 4) off the top of my head:

  1. this.toString() or this["toString"]()
  2. b.toString() or b["toString"]()
\$\endgroup\$
  • \$\begingroup\$ Thanks Daniel! You are right about BatchCacheRequest - but I will be using that soon! And awesome; I'll give that a shot. Thanks again. \$\endgroup\$ – keldar Nov 20 '13 at 15:52
2
\$\begingroup\$

The way to handle the namespace looks fine.

Instead of putting the reference in the window object, you can just return it out to the global scope and declare it there. That makes it a little clearer that the entire goal for the function is to create a single identifier in the global scope.

As you are only using the b reference once, you can just return the object directly:

var batch = (function(){

  var info = [];

  function BatchInfo(command, url, data) {
    ...
  }

  return {

    clear: function () {
      ...

    ...

  };

}());

You might consider to return the object itself from the add method, instead of the info array. That way the method calls can be chained:

add: function (command, url, data) {
  info.push(new BatchInfo(command, url, data));
  return this;
},

Example:

var s = batch.
  add('cmd1', 'url1', 'data1').
  add('cmd2', 'url2', 'data2').
  add('cmd3', 'url3', 'data3').
  add('cmd4', 'url4', 'data4').
  toString();
\$\endgroup\$
  • \$\begingroup\$ I understand the suggestion for var batch = ... but it seems like personal preference. Great suggestion for the chaining though. That could be quite handy, though in that case maybe it should be expanded to allow objects to be passed. \$\endgroup\$ – Daniel Nov 20 '13 at 17:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.