简体   繁体   中英

Can someone fluent in Javascript explain to me whats going on here SIMPLY

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); })('');
}

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 :)

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() .

(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 */)

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.

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>

"#" 是颜色代码 (#7b7b6e) 的一部分,该函数返回一个类似于 "7b7b6e" 的随机字符串,并在其前面添加 #

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM