2

I am trying to understand lines 10 and 12 of the below code, and have given my attempt to explain. Please correct me as applicable.

//Line 10
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
//Line 12
return methods.init.apply(this, arguments);

Line 10. If the argument provided to samplePlugin is a method, then make that method have the same this as is at the current script's location (i.e. Line 10), and ... Help! Where is arguments defined? What is with the call()? EDIT. Okay, it seems Array.prototype.slice.call(arguments, 1) makes a real array out of all the arguments except the first one, however, still is a bit of a mystery.

Line 12. Else if the argument provided to samplePlugin is an object or empty, then make the init method have the same this as is at the current script's location (i.e. Line 12), and ... Seems simpler, but still need help...

(function($){
    var methods = {
        init    : function (options) {return this.each(function () {/* ... */});},
        method1 : function ()        {return this.each(function () {/* ... */});},
        method2 : function ()        {return this.each(function () {/* ... */});}
    };

    $.fn.samplePlugin = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));   //Line 10
        } else if (typeof method === 'object' || ! method) {
            return methods.init.apply(this, arguments);                                     //Line 12
        } else {
            $.error('Method ' +  method + ' does not exist on jQuery.samplePlugin');
        }    
    };
    }(jQuery)
);
user1032531
  • 24,767
  • 68
  • 217
  • 387
  • 2
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments – SLaks Mar 11 '18 at 22:00
  • 1
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call – SLaks Mar 11 '18 at 22:00
  • Related: https://stackoverflow.com/q/15455009/3001761, https://meta.stackoverflow.com/q/253894/3001761. – jonrsharpe Mar 11 '18 at 22:02
  • @SLaks Okay, that gets me going forward a little bit. So, `arguments` is a keyword representing for an array like object holding the arguments passed to the function. I will try to use this to better understand the rest. I've used these few lines for years without really understanding them, and I feel that it is about time for this to stop. – user1032531 Mar 11 '18 at 22:04
  • Related: https://stackoverflow.com/questions/7056925/how-does-array-prototype-slice-call-work – user1032531 Mar 11 '18 at 22:08

1 Answers1

4

You unraveled some parts yourself, but there's a non-trivial part of JavaScript function trickery involved. Let's put them apart:

arguments is a “magic” variable inside function bodies, that offers access to the function arguments. It's simply there without declaration, and works like an array but is no array (will become important in a moment).

If you want to access the first argument to a function, arguments[0] is the place to look. In your specific function, that’s always identical to the variable method.

If you want to get any additional arguments (remember, that you can call JS functions with any number of arguments, not just the ones declared), a first pass might look like this:

arguments.slice(1)

but, remember, arguments is no array, it just looks like one. All Array methods are missing. So we need to borrow them from Array.prototype, where “real” arrays get them from:

Array.prototype.slice

To get slice to work with arguments, it must be called with arguments as this:

Array.prototype.slice.call(arguments, 1)

(Read up on Function.prototype.call)

So far so good.

Now, imagine the jQuery plugin gets called like this:

$('body').samplePlugin('method1', 'foo', 'bar');

The variable method is now "method1". The first if is triggered, because there is a method1 key in methods, and it's a function. So the function is called with all remaining arguments:

methods['method1'].apply(this, ['foo', 'bar']);

Second branch: If the plugin is called like this:

$('body').samplePlugin({ foo: 'bar' });

then method == { foo: 'bar' } and we're landing in branch 2, since it's an object. The idea of the boilerplate author is, that in this case the init method should be called:

methods['init'].apply(this, arguments);

(methods['init'] is the same as methods.init in JS.) We do not need the splicing trick from above, since arguments[0] is not any method name.

Boldewyn
  • 81,211
  • 44
  • 156
  • 212
  • No problem. It's 11PM here, so I might not be around in a moment, but do post questions, if you have them. – Boldewyn Mar 11 '18 at 22:23
  • 1
    Ah ha! I just tested that `myRealArray.slice(1)` and `Array.prototype.slice.call(myRealArray,1)` result in the same. Things are a little clearer! – user1032531 Mar 11 '18 at 22:46