【问题标题】:NaN return value from function Javascript || Function Execution Order来自函数 Javascript 的 NaN 返回值 ||函数执行顺序
【发布时间】:2016-08-15 06:48:39
【问题描述】:

这里是 Javascript 新手,经过数小时挖掘其他问题后,老实说,我不太确定如何解释,但我会尽力而为,希望您能帮助我。

HTML:

<div id='header'> <h1> Pastel Land </h1> </div>

    <div id='container'>

        <div id='readyContainer'> 

            <h3> This game will start in </h3>
            <h1 id='readySeconds'> </h1>

        </div>

        <div id='shape'> </div>

    </div>

    <div id='features'>

        <button id='start'> START </button> 
        <button id='stop'> STOP </button>

        <p id='timeBox'></p>
        <p id='timeAverageBox'></p>
    </div>

    <div id='testbox'> </div>

完整的脚本:

document.getElementById('start').onclick = function () { 
        document.getElementById('readyContainer').style.display = 'block';
        document.getElementById('readySeconds').innerHTML = '3'
        setTimeout(function() {document.getElementById('readySeconds').innerHTML = '2'}, 1000);
        setTimeout(function() {document.getElementById('readySeconds').innerHTML = '1'}, 2000);


        setTimeout(readyAlert,3000);
        setTimeout(displayShape, 3000);


        var style = document.getElementById('shape').style;

        var el = document.getElementById('shape');

        el.addEventListener("click", a, false);
        el.addEventListener("click", b, false);

        function a() {

            style.display = "none";
            displayShapeDelay(); // calls the delay function
        }

        function b() { 

            end = new Date().getTime();  // saves time when clicked

            var time = (end - start)/1000 ; // calculates interval from shape creation until click

            document.getElementById('timeBox').innerHTML = time + 's';

            return time;

        }

        document.getElementById('testbox').innerHTML = b();


        function readyAlert() {
                document.getElementById('readyContainer').style.display = 'none';

        }   

        function getRandomColor() {

        var hex = ["#96ceb4", "#ffeead", "#ff6f69", "#ffcc5c", "#88db8b0", "#528491"];

        var color = hex[Math.floor(Math.random() * 6)]; // generates integer numbers [0,5], selects indexed item from hex

        return color;

        }



        function displayShape () {

            var percentages = [];

            for (var i=0; i<4; i++){ // generates a list with 4 different random integer values [5,60]

                percentages.push((Math.floor(Math.random() * 61) + 5));

            }

            var width = (Math.floor(Math.random() * 61) + 5); // generates integer numbers [5,60]

            var shapeRand = Math.random()

            if (shapeRand < 0.3) { // circle
                style.borderRadius = "50%";

            } else if (shapeRand >= 0.3 && shapeRand < 0.6) { // random shape
                style.borderTopLeftRadius = percentages[0] + "%";
                style.borderBottomRightRadius = percentages[1] + "%";
                style.borderTopRightRadius = percentages[2] + "%";
                style.borderBottomLeftRadius = percentages[3] + "%";
            } else { // square
                style.borderRadius = "0%";
            }

            //general shape styles
            style.width = width + "%";
            style.height = width + "%";
            style.display = "block";
            style.backgroundColor = getRandomColor();
            style.top = percentages[0] + "%";
            style.left = percentages[3] + "%";

            start = new Date().getTime(); // saves time when shape is created

            console.log(width);
            console.log(getRandomColor());
            console.log(shapeRand);
            console.log(percentages);


        }

        function displayShapeDelay () { // calls the main function with a delay between ]0s,2s[
            setTimeout(displayShape, Math.random() * 2000);
        }


        document.getElementById('stop').onclick = function() {


        }

    }

在我有这个之前:

我的目标是将 var 'time' 返回到全局范围,以便我可以使用它来创建一个数组,其中包含每次单击创建的每个值。我已经意识到匿名函数不可能做到这一点。

document.getElementById('shape').onclick = function() { // calls the delay function

            style.display = "none";

            displayShapeDelay();

            end = new Date().getTime();

            time = (end - start)/1000 ;

            document.getElementById('timeBox').innerHTML = time + 's';

            return time

        }

这就是我现在的代码:

var shapeClick = document.getElementById('shape');

        shapeClick.addEventListener("click", a, false);
        shapeClick.addEventListener("click", b, false);

        function a() {

            style.display = "none";
            displayShapeDelay(); // calls the delay function
        }

        function b() { 

            end = new Date().getTime();  // saves time when clicked

            var time = (end - start)/1000 ; // calculates interval from shape creation until click

            document.getElementById('timeBox').innerHTML = time + 's';

            return time;

        }

        document.getElementById('testbox').innerHTML = b();

现在,这里有几个问题:

1- 我似乎无法理解为什么在按下“开始”按钮后会为两个“时间 div”分配值。这意味着函数 b 正在运行,但它不应该只在 onClick 事件之后运行吗?

2- 在“第一轮”中,我理解为什么两个值都显示为 NaN,因为还没有为变量“time”分配值。但是在 onClick 事件执行后,在 'timeBox' 内分配的 'time' 值可以正常工作,但在函数外部调用的值却不行。函数b中的“返回时间”不是应该返回“时间”变量的值吗?

提前致谢!

【问题讨论】:

  • 你能添加你的 HTML 吗?
  • 1:b() 运行的原因是因为这一行“document.getElementById('testbox').innerHTML = b();”。它在我假设的全局范围内,所以当代码加载时 b() 会立即执行..你最好把它放在 b() 中,所以 "document.getElementById('testbox').innerHTML = time ;"
  • 2: b() 返回时间,我假设您谈论的 testbox 元素没有随时间更新,那是因为 "document.getElementById('testbox').innerHTML = b ();"仅在初始加载时运行一次。当 b() 再次运行时它不会更新。看看我的其他评论会解决这个问题。
  • @whipdancer HTML 添加。
  • 答案已更新以解决代码结构问题。发现 Pastel Land 有点像 Whac-a-Mole 街机游戏。 @whipdancer

标签: javascript function variables scope


【解决方案1】:

粉彩土地

onclick 函数充当大量代码的外部函数,这些代码在单击开始按钮时执行。每次单击开始按钮时,它都会运行:快速单击开始多次以查看问题。 .

在 #start.onclick() 你有

document.getElementById('testbox').innerHTML = b();

在主线单击处理程序代码中:它不在另一个函数中,并在单击开始按钮时运行。由于尚未设置end,因此time 的结果为NaNfunction b里面的代码也设置了#timebox的内容。

如果您在严格模式下运行代码,javascript 引擎会告诉您end 尚未声明。它应该是 - 即使在全球范围内需要。

顺便说一句,Date.now() 避免了创建和丢弃 Date 对象的需要,相当于new Date().getTime()

我建议重新编写代码以将 Pastel Land 的逻辑移到开始按钮单击处理程序之外,并根据需要将单击处理程序调用到主应用程序代码中,但仅包含特定于开始操作本身的逻辑。如果您想避免污染全局范围,您可以将所有代码包含在 IIFE(立即调用的函数表达式)中,该代码将提供与单击处理程序当前提供的相同范围包含规定。好心,我认为当前状态的代码呈现x-y problem :-)


游戏

Pastel Land 的重组版本如下所示,原因如下:您有时间亲自尝试,大部分代码是您的,其余代码演示了建议的含义。这是一个非常愚蠢的游戏,值得一玩!

<!DOCTYPE html>
<html>
<head>
<title>PastelLand</title>
<meta charset="utf-8">
<script>

window.addEventListener("load", function() // an IIFE
{"use strict"

   // Pastel Land

    var running = false;
    var start = 0;
    var end = 0;
    var times = []; // calculating average still to do
    var el, style;

    function getRandomColor() {
        var hex = ["#96ceb4", "#ffeead", "#ff6f69", "#ffcc5c", "#88db8b0", "#528491"];
        var color = hex[Math.floor(Math.random() * 6)]; // generates integer numbers [0,5], selects indexed item from hex
        return color;
    }

    function displayShape () {
        var percentages = [];

        for (var i=0; i<4; i++){ // generates a list with 4 different random integer values [5,60]
            percentages.push((Math.floor(Math.random() * 61) + 5));
        }
        var width = (Math.floor(Math.random() * 61) + 5); // generates integer numbers [5,60]
        var shapeRand = Math.random()

        if (shapeRand < 0.3) { // circle
            style.borderRadius = "50%";

        } else if (shapeRand >= 0.3 && shapeRand < 0.6) { // random shape
            style.borderTopLeftRadius = percentages[0] + "%";
            style.borderBottomRightRadius = percentages[1] + "%";
            style.borderTopRightRadius = percentages[2] + "%";
            style.borderBottomLeftRadius = percentages[3] + "%";
        } else { // square
            style.borderRadius = "0%";
        }

        //general shape styles
        style.width = width + "px";
        style.height = width + "px";
        style.position = "absolute"
        style.display = "block";
        style.backgroundColor = getRandomColor();
        style.top = percentages[0] + "%";
        style.left = percentages[3] + "%";

        start = Date.now(); // saves time when shape is created

        console.log(width);
        console.log(getRandomColor());
        console.log(shapeRand);
        console.log(percentages);
    }

    function displayShapeDelay () { // calls the main function with a delay between ]0s,2s[
        setTimeout(displayShape, Math.random() * 2000);
    }

    function readyAlert() {
            document.getElementById('readyContainer').style.display = 'none';
    }

    function userFound() {
        style.display = "none";
        end = Date.now(); 
        var time = (end - start)/1000 ; // calculates interval from shape creation until click
        document.getElementById('timeBox').innerHTML = time + 's';
        displayShapeDelay(); // calls the delay function
        times.push( time); // saves time user took to find shape
    }

    function userStart() {

        if( running)
            return;
        running = true;
        document.getElementById('readyContainer').style.display = 'block';  
        document.getElementById('readySeconds').innerHTML = '3'
        setTimeout(function() {document.getElementById('readySeconds').innerHTML = '2'}, 1000);
        setTimeout(function() {document.getElementById('readySeconds').innerHTML = '1'}, 2000);
        setTimeout(readyAlert,3000);
        setTimeout(displayShape, 3000);
        times.length = 0;  // reset times array
    }

    function userStop() {
        running = false;
        style.display="none"
    }
    function init() {
        el = document.getElementById('shape');
        style = el.style;
        el.addEventListener("click", userFound, false);
        document.getElementById('start').onclick=userStart;
        document.getElementById('stop').onclick=userStop;    
    }
    return init; // window load listener
}());
</script>
</head>
<body>
<div id='header'> <h1> Pastel Land </h1> </div>
<div id='container'>
    <div id='readyContainer'>
        <h3> This game will start in </h3>
        <h1 id='readySeconds'> </h1>
    </div>
    <div id='shape' style="height:40px;width:40px;"></div>
</div>
<div id='features'>
    <button id='start'> START </button> 
    <button id='stop'> STOP </button>
    <p id='timeBox'></p>
    <p id='timeAverageBox'></p>
</div>
<div id='testbox'> </div>
</body>
</html>

平均值的计算和显示以及页面显示的微小更改仍有待完成(我不是在编写它们!)。宽度和高度的单位从“%”更改为“px”(像素)和“位置:绝对;”添加到 shape.style。函数ab 合并为函数userFound。省略了涉及“testbox”的代码。 IIEF 返回一个在窗口加载后执行的初始化函数。


注意事项

userStartuserStop 单击事件处理程序是使用命名的 function declarations 定义的,而不是在调用其他函数的参数列表中将它们编码为匿名函数。

声明的函数名是指通过它们的声明创建的函数对象。因此,将元素的 onclick 属性的值设置为函数名称是可行的,因为该属性需要函数对象值。将onclick 设置为调用其中一个处理程序返回的未定义值将不起作用。

开始/停止处理程序可能已注册为“单击”事件侦听器,使用 addEventListener 而不是元素 onclick 值。

init 函数被 IIFE 调用。 IIFE 本身在

window.addEventListener("load", function() { // IIFE code }() );

语句在页面的头部执行。此时会创建在 IIFE 中在顶层声明的所有函数对象。 IIFE 返回的函数init 注册为侦听器,以便在为 HTML 正文创建所有 DOM 元素并触发窗口加载事件之后执行。

如果在其他elstyle 初始化之后,从init 调用userStart,游戏将开始。虽然userStart 通常会在点击开始按钮时被调用,但如果通过其他方式调用,它的行为仍然相同。

【讨论】:

  • 很抱歉回复晚了,真的很感谢你加倍努力。如果您不介意的话,我刚刚找到了一些时间来修改您的代码并对 init 函数进行了一些跟进。为什么只需要启动事件处理程序的函数名?如果我改用“userStart()”,即使没有单击开始,为什么函数会在加载时开始运行?我知道 IIEF 调用了 init 函数,但是不应该调用 onclick 事件的函数,嗯...... onclick。再次感谢您!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 2020-04-05
  • 2020-09-07
相关资源
最近更新 更多