1

Im taking a course on udemy and I came across this code that changes the background of a window. The thing is the function randColor loses me. Id like to know exactly whats going on.

I know a function called randColor is declared, then the function itself RETURNS a function + # but I am trying to understand the logic of how it all happens. There is a HASH symbol that is added and I believe its also an IIFE correct?

I very much appreciate the help!

document.querySelector("button").addEventListener("click", function(){
  document.body.style.background = randColor();
})


function randColor(){
  return '#' + (function co(lor){   return (lor +=
    [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)])
    && (lor.length == 6) ?  lor : co(lor); })('');
}
Twisted
  • 17
  • 1
  • 6
  • 4
    Whoa, what horrible code. It's a recursive IIFE instead of a simple loop, with an obfuscated one-liner body instead of two statements, with a weirdly placed base case condition. – Bergi Oct 01 '17 at 19:05
  • @Bergi What is "horrible" about the code? – guest271314 Oct 01 '17 at 19:06
  • 1
    @guest271314 The fact that you need to explain it. It does a trivial thing in a hard-to-understand way. – Bergi Oct 01 '17 at 19:07
  • @guest271314 - Would you agree that the same can be achieved by straight forward coding without any loss of efficiency? – PM 77-1 Oct 01 '17 at 19:10
  • @PM77-1 I would say it can be achieved in a straight-forward way and *gain* efficiency – Bergi Oct 01 '17 at 19:10
  • @Bergi Where is "efficiency" within text of original Question? – guest271314 Oct 01 '17 at 19:11
  • @guest271314: Speed and storage. – PM 77-1 Oct 01 '17 at 19:13
  • I once wrote this type of code (in a different language) and it was later used during interviews: "What does it do and why you should not write your code in this fashion?".. – PM 77-1 Oct 01 '17 at 19:17
  • Would this be considered _efficient_? `'#'+(Math.round(Math.random()*'0xffffff')).toString(16);` – blex Oct 01 '17 at 19:19
  • @PM77-1 The present inquiry is not a job interview, nor are benchmarks included at OP which display the different approaches tried as to a prospective estimation of "efficiency". Many do whatever their employer tells them to do. Perhaps post the same inquiry that was included in the "interviews" and same code at OP as a Question? The present Question does not present the inquiry as having any relevance to "efficiency". – guest271314 Oct 01 '17 at 19:20
  • 1
    @blex No, that doesn't work. You need `.padStart(6, "0")` (and also you shouldn't multiply by a string) – Bergi Oct 01 '17 at 19:23
  • @PM77-1 https://stackoverflow.com/questions/46516234/what-is-the-most-efficient-approach-to-compose-a-string-of-length-n-where-random – guest271314 Oct 01 '17 at 19:45
  • @Bergi _"I would say it can be achieved in a straight-forward way and gain efficiency"_ https://stackoverflow.com/questions/46516234/what-is-the-most-efficient-approach-to-compose-a-string-of-length-n-where-random – guest271314 Oct 01 '17 at 19:47

6 Answers6

4

The goal is to generate a random color in the Hex format. My attempt to explain the code you provided us with:

When randColor is called it is added to the call stack and then gets paused waiting for the nested function's calls to complete.

That nested function co(lor) is IIFE and it is called recursively. Initially an empty string is passed in and the local lor variable is assigned to it.

Math.floor(Math.random()*16) - generates numbers from 0 to 15 which are the indexes of the array [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']. At each step a new symbol from the array is added to the local lor string passed in as a parameter earlier. Updated string is passed further into a new call if its length is fewer than 6.

The nested function's recursion adds to the call stack objects with lor values like this (for example):

5aTh46 (top, last call, gets popped out first)
5aTh4
5aTh
5aT
5a
5      (first call)

when the length of the local lor variable gets equal to 6 after the 6th call, then base condition lor.length == 6 is fulfilled and 5aTh46 string is returned from the top of the call stack. So, for the 5th call we have

return (lor += [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'] [Math.floor(Math.random()*16)]) && (lor.length == 6) ? lor /* = 5aTh4*/ : co(lor) /* = 5aTh46*/;

lor.length == 6 is false since local lor is equal to 5aTh4. So, 5aTh46 returned by the 6th call is returned after the 5th call as well and so forth untill value 5aTh46 is finally returned as a result of co(lor)'s calls and added to the # string inside randColor. In the end we get #5aTh46.

PS. That's how I understand it. If you get the concepts of event loop and IIFE this explanation may sound simple :)

curveball
  • 4,320
  • 15
  • 39
  • 49
  • Thanks for your help I do appreciate it - im just lost because the part after return is what throws me off - why after return is there an OPENING parantheses? I never knew that was valid javascript. Also why are the arrays not separated by commas? return (lor += [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)]) && (lor.length == 6) ? lor : co(lor); ^ so thats saying lor = lor+ the letters/numbers MULTIPLIED by a whole number between 1 and 16 AND if the length of lor == 6, then what? – Twisted Oct 01 '17 at 23:23
  • Briefly, 1) after return is there an OPENING parantheses - it is a part of IIFE which in this case looks like `(function(){})()`. It is also ok like this `function(){}()` if the whole thing is a part of an expression. 2) why are the arrays not separated by commas? - there is only 1 array of symbols. The other pair of `[]` contains calculated index. it is like `['a', 'b'][1]` (= 'b') but that index `1` is calculated on the fly. – curveball Oct 01 '17 at 23:37
  • 3) return (lor += [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Mat‌​h.random()*16)]) && (lor.length == 6) ? lor : co(lor); - this is the use of conditional operator `[cond to evaluate] ? [if true] : [if false]`. The whole thing says: add one more symbol to `lor` AND if `lor`'s length is 6 return `lor`. Otherwise keep calling `co(lor)` (untill its length reaches 6 symbols). – curveball Oct 01 '17 at 23:38
  • so, things to master: IIFE, arrays, conditional operator and recursion. – curveball Oct 01 '17 at 23:39
  • Ok I think Im getting it so I did some testing in codepen - basically this whole part is a ternary operator which is the better version of an if/else statement---- return (lor += [0,1,2,3,4,5,6,7,8,9,'a','b','c','d', 'e','f'][Math.floor(Math.random()*16) ]) && (lor.length == 6) ? lor :co(lor);--- Its saying - IF lor = lor + random string AND the length is === 6, THEN lor ELSE co(lor) right? – Twisted Oct 02 '17 at 02:26
  • Yes, it is also called ternary operator since it involves 3 operands. – curveball Oct 02 '17 at 06:25
0

Yes. It is an IIFE. The invocation starts with a ''. Then recursive calls are made which appends 1 character at a time from the array [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'] randomly. Six such characters are added( checked by (lor.length == 6)). The preceeding '#' is prepended to the 6 character to form a random color which is returned by the function randColor().

Rajeev Ranjan
  • 3,588
  • 6
  • 28
  • 52
0
(function co(lor /* passed empty string */){ /* concatenate result of `co("")` */  return (lor +=
    [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)])
       // if `lor` string `.length` is `6` return `lor` string 
       // else call `co` with `lor` as parameter
    && (lor.length == 6) ?  lor : co(lor); })('' /* empty string */)
guest271314
  • 1
  • 15
  • 104
  • 177
0

The code at first looks terrible. Let simplify it step by step.

//find 1st <button> element and assign on'click' handler to it
document.querySelector("button").addEventListener("click", function(){
  document.body.style.background = randColor();
  //background is the **result** of execution of randColor()
})


function randColor(){
  return '#' + (function co(lor){   return (lor +=
    [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)])
    && (lor.length == 6) ?  lor : co(lor); })('');
}

randColor() returns an expression which includes a function (in fact, IIFE).
Let put the result instead of function call. Meanwhile, parenthesis around the function can be omitted here because of + operator before.

document.querySelector("button").addEventListener("click", function(){
  document.body.style.background = '#' + function co(lor){
   return (lor +=
    [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)])
    && (lor.length == 6) ?  lor : co(lor); }(''/*send empty string to lor parameter*/);
})

Still not easy? Let's take next step.

document.querySelector("button").addEventListener("click", function(){
  document.body.style.background = '#' + co(''/*send empty string to lor parameter*/);
})

function co(lor){
   return (lor += /*assignment returns the new value. always truey in this case*/
    [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)/*magic 16 is the array length*/])
    && (lor.length == 6) ?  /*returns when we have 6 character string*/lor : /*call the function recursively*/co(lor); 
}

And final simplification. Use a loop instead of recursion.

function co(){
   var lor = '';
   var hexDigits = '0123456789abcdef'.split('');
   for(var i = 0; i < 6/*required length*/;i++){
     lor += hexDigits[Math.floor(Math.random() * 16)/*0 - 15*/];
   }
   return lor;
}

Leading # makes up correct HEX value for the color.

Alex Kudryashev
  • 9,120
  • 3
  • 27
  • 36
0

i would like to propose a more simplified iterative code instead of the recursion.

   <script type="text/javascript">
    document.querySelector("button").addEventListener("click", function(){
        // body
        document.body.style.background=randcolor();
    })

    function randcolor(){

        var list=['1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f'];
        var r='#';
        while(r.length<7)
        {
            r+=list[Math.floor(Math.random()*16)];
        }
        return r;
    }

</script>
-3

"#" is a part of color code (#7b7b6e), the function returns a random string something like this "7b7b6e" and # is added infront of it

JefinOJ
  • 13
  • 6