4

Here is the code I have encountered:

    var c = function() {},
        c = {
            autoSelectFirst: !1,
            appendTo: "body",
            serviceUrl: null,
            lookup: null,
            .. etc..
            lookupFilter: function(a, b, c) {
                return -1 !== a.value.toLowerCase().indexOf(c)
            },
            .. etc..
        };

Why is c first declared as an empty function then re-declared as an object? Doesn't that just overwrite the function declaration? I suppose it is a fundamental JS construct.

Oliver Williams
  • 5,966
  • 7
  • 36
  • 78
  • 11
    That looks like code written by somebody who really didn't know what they were doing, or else the result of some sort of edit mistake. – Pointy May 05 '16 at 21:43
  • 1
    Where did you encounter it? Was the code minified? Looks like it could be an artefact of an automated code generator of some sort. The variable and parameter names sure make it look that way. Can you give some context? –  May 05 '16 at 21:47
  • one day in a distant future I will learn all ways of declaring a function in js. Added another one today. I wonder what else lures in the dark corners of the web... :D – Nelson Teixeira May 05 '16 at 21:53
  • I also ask you to tell us where did you encounter this thing. :) – Nelson Teixeira May 05 '16 at 21:54
  • It should also be mentioned that the first part `var c = function()` created a *function object*, then the part after the comma, created a *normal object*, as a simple `typeof(c)` can show. This thing can create a lot of confusion. Please avoid it at all costs. Hope this doesn't spread up. Maybe this question should be deleted ? What you guys think ? – Nelson Teixeira May 05 '16 at 22:08
  • I guess technically there could be a side-effect if you're in the global variable scope, and `window.c` had a "setter" function in its descriptor. It would then be invoked twice, but this is *extremely* unlikely. –  May 05 '16 at 22:08
  • ...though I still say it's most likely leftover from a minification process. –  May 05 '16 at 22:09
  • 2
    This was a deminified version of jquery.autoselect.min.js - version 1.2.9 – Oliver Williams May 05 '16 at 22:11
  • I can't find it online.. can you provide the url ? – Nelson Teixeira May 05 '16 at 22:16
  • 1
    @NelsonTeixeira: I found one, [minified](http://www.ftav.com/wp-content/plugins/yith-woocommerce-ajax-search/assets/js/yith-autocomplete.min.js?ver=1.2.7). Do a text search in the document for `c=function(){}` and you'll see this around the first or second line. Indeed it's nothing more than output from a minifier, as the code is clearly not hand-written that way. –  May 05 '16 at 22:49
  • oh my God, it's in the wild already! :D Why would a minifier do a thing like that ? – Nelson Teixeira May 05 '16 at 22:50
  • 2
    @NelsonTeixeira: Here's an [unminified version](http://www.ftav.com/wp-content/plugins/yith-woocommerce-ajax-search/assets/js/yith-autocomplete.js?ver=1.2.7). If you search for `autoSelectFirst`, you'll find the source of the original function, which is `noop = function(){}`, which makes sense that it would be a no-operation function. Apparently this is only used for the default value in the object literal being created below it, so the `c` is still referencing the func during its creation. That's actually pretty darn clever of the minifier to know it could reuse that variable name like that. –  May 05 '16 at 23:00
  • 1
    It's an obvious stupid mistake, but it happens. – Bekim Bacaj May 05 '16 at 23:07
  • @squint understood... I take back what I said. It's correct. But please don't use this outside of minifiers or you may give your team mates a really hard time. – Nelson Teixeira May 05 '16 at 23:07
  • @BekimBacaj take a look at the examples squint provided and you'll see that it's correct. But only in minifiers :) – Nelson Teixeira May 05 '16 at 23:09
  • @NelsonTeixeira, there's absolutely nothing correct about immediately re-declaring the same variable name, except, if it's deliberately written for targeting a certain browser engine and harm its overall performance. – Bekim Bacaj May 05 '16 at 23:13
  • @BekimBacaj have you understand it after squints answer below ? – Nelson Teixeira May 05 '16 at 23:25
  • Yes I do understand (I always did ) that it is an old trick of fooling certain JITs and giving them unnecessary overhead. – Bekim Bacaj May 06 '16 at 00:50
  • It has nothing to do with the JIT compiler. –  May 06 '16 at 01:18

2 Answers2

7

I found the file in question online, and it turns out there's a good reason for this.

First of all, it should be noted that the code in the question is from a minified source that has been indented.

Here's a chunk of the code:

var c=function(){},c={autoSelectFirst:!1,appendTo:"body",serviceUrl:null,lookup:null,
onSelect:null,width:"auto",minChars:1,maxHeight:300,deferRequestBy:0,params:{},
formatResult:g.formatResult,delimiter:null,zIndex:9999,type:"GET",noCache:!1,
onSearchStart:c,onSearchComplete:c,onSearchError:c,
// -----------^------------------^---------------^

So this is the code in the question, but a little more of it. The important thing to notice is that the variable c is used in the creation of the object literal, with onSearchStart:c,onSearchComplete:c,onSearchError:c,.

So which value is c assigning in the object? Because the object is still in the process of being created, that means c is still referencing the function, so properties like onSearchStart, which seem to be event handlers are getting the empty function as a default.

This makes much more sense.

To verify, I also found the original, unminified source. Here's the related code:

//   v---originally it's called `noop`
var noop = function () { },
    that = this,
    defaults = {
        autoSelectFirst: false,
        appendTo: 'body',
        serviceUrl: null,
        lookup: null,
        onSelect: null,
        width: 'auto',
        minChars: 1,
        maxHeight: 300,
        deferRequestBy: 0,
        params: {},
        formatResult: YithAutocomplete.formatResult,
        delimiter: null,
        zIndex: 9999,
        type: 'GET',
        noCache: false,
        onSearchStart: noop,    // <---here it's being used
        onSearchComplete: noop, // <---here it's being used
        onSearchError: noop,    // <---here it's being used

So it's clearer now that the noop, which generally stands for no-operation, has its own name and is indeed being used in the object being created just below it. Also, this is the only place noop is used in the entire file.

So apparently the minifier was clever enough to see that the variable originally called noop was not going to be used any longer, so it was free to reuse that variable name for the object. An impressive bit of code analysis, IMO.

  • 1
    Please edit the OP's question to show the rest of the code. As the code is shown in the original question, there is no point to the initial `c` declaration which is what the other answer addresses. But, when you add more to the question, then there becomes a point. – jfriend00 May 05 '16 at 23:16
  • This were also impressive Google skills to find these files ;) – Nelson Teixeira May 05 '16 at 23:16
  • @NelsonTeixeira: I lucked out. Google came up with only one result when I used `filetype: js`. –  May 05 '16 at 23:17
  • 2
    Amazing find and analysis. – Prusprus May 05 '16 at 23:17
  • @jfriend00: Fair point. This is technically an alteration of the question. As it stands, your answer is still more of a direct response. I don't want to alter the meaning of the question at this point, but I thought the info would be worth noting. –  May 05 '16 at 23:18
  • @squint - OK, I've modified my answer to make sure it is addresses the actual code in the question. – jfriend00 May 05 '16 at 23:19
  • @OliverWilliams clearly this should be the accepted answer. – Knu May 05 '16 at 23:45
  • @Knu: Thanks, but I'm not sure he should change it. I really did add to the question by providing more code not there originally, which normally shouldn't be done. The current accepted answer deals with the given code more directly. I was just glad to know that there was ultimately some sensible reason for this. –  May 05 '16 at 23:53
  • Both answer are technically correct. @jfriend00 is correct in that the provided code had no practical benefit to defining the variable twice consecutively, but squint is correct in that the larger context of the code, which the example code was pulled from, the double definitions has some value to it. Thinking long-term for this question, it become more relevant to display the full code in the question, and have your answer marked as correct, as that's the bigger lesson to be learned here. But, at the same time, jfriend00 should not be penalized himself. – Prusprus May 06 '16 at 00:35
  • 2
    I should add that I would consider it an anti-pattern to actually manually write code like this. It's very misleading and not immediately obvious to someone reading the code what is going on. Code generators creating code that is not meant for human consumption are allowed to do optimization tricks that are not meant to be readable, but humans should not write code like this for other humans to read. For human readable code, an optimization like this is not an advantage over making the code easy to understand and maintain for those who come after. – jfriend00 May 06 '16 at 00:38
  • I agree. Although for future visitors to this question, in keeping that others might parse through minified code and ask themselves the same question, I believe that this question and answer should address how that variable is being used in its minified context, rather than how this is to the detriment of readability or code effectiveness, in the mindset that code similar to this would have been manually written by a programmer. – Prusprus May 06 '16 at 00:43
  • @squint says: "So which value is c assigning in the object? Because the object is still in the process of being created, that means c is still referencing the function,...event handlers are getting the empty function as a default." It is assigning itself (to its own properties), not the function! The function is a history, already overwritten. No sane coder would assign an anonymous function to events knowing that the value & type of any event is the null object. Especially not when he'd already overwritten it. What is being created is in fact a circular reference to the same object. – Bekim Bacaj May 06 '16 at 00:43
  • 1
    @BekimBacaj: No, it's [not overwritten yet](https://jsfiddle.net/oet21p29/), and therefore not a circular reference. Either way, this has nothing to do with being a "sane coder" because, as the answer states, the code is from a minification process and was not originally hand-written that way. –  May 06 '16 at 01:16
  • @Prusprus: Meh, accepted answers, points, etc. is all just distraction. That's why I make all my answers *community wiki*. No concern about points and anyone can add to/improve it. :-) –  May 06 '16 at 01:28
  • Yes, you are correct. Function declarations and declared variables get special treatment and precedence over object literals and similar creations. Meaning, the c is still pointing to the function as object literal is still being parsed. I stand corrected. – Bekim Bacaj May 06 '16 at 01:36
  • @all, I appreciate the effort that has gone into this. I hope that this is a) findable and b) a question others might be asking (declaring a variable twice). – Oliver Williams May 06 '16 at 12:14
  • @BekimBacaj you have a view point that can be useful to further understand the problem. Why don't you answer the question with your full view on this subject ? – Nelson Teixeira May 06 '16 at 16:35
2

Based on this code example from the original question:

var c = function() {},
    c = {
        autoSelectFirst: !1,
        appendTo: "body",
        serviceUrl: null,
        lookup: null,
        .. etc..
        lookupFilter: function(a, b, c) {
            return -1 !== a.value.toLowerCase().indexOf(c)
        },
        .. etc..
    };

The c = function() {}, part in the code that is included in your question has no point. It's like doing:

var x = 1, x = 3;

There's no point in assigning one value to the variable and then immediately assigning another value to it.

The code you show would generate the exact same result (with less confusion) as this:

    var c = {
        autoSelectFirst: !1,
        appendTo: "body",
        serviceUrl: null,
        lookup: null,
        .. etc..
        lookupFilter: function(a, b, c) {
            return -1 !== a.value.toLowerCase().indexOf(c)
        },
        .. etc..
    };

Based on the comments for where this code was found, it appears that it is part of some automatic code generation or minimization process.

FYI, Javascript does not use the phrase "associative array". The second declaration is a Javascript object.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • please see @squint comment on the question. It shed light on why it's done like that. – Nelson Teixeira May 05 '16 at 23:10
  • +1 worth having an upvote for your answer, which is correct for the context of the original question, though I believe that the correct answer should be attributed to @squint, for the reason of his answer being correct in regards to the (not previously known) larger context of the code. – Prusprus May 06 '16 at 00:37