JS|初始化对象在函数访问后未定义 运行 多次

JS| Initialised object is undefined after function accessing it is run more than once

我正在尝试减少 Chrome 扩展中的全局变量以减少 'spaghettiness' 或 JavaScript.

中的歧义

我试图通过一个 init 函数来尝试此操作,该函数在 mousedown eventListener 回调函数中声明这些其他全局变量,该回调函数可能会被触发不止一次。

这样我就可以将这些变量 + 事件作为此类回调的参数传递给其他 eventListener 回调(即 mouseup 回调)。

我已将问题分解到一个单独的文件中:

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>
  <script src="test.js"></script>
</body>
</html>

test.js

isVarsInitOnce = false;

function executesMoreThanOnce() {
    const initVariables = function() {
        console.log('runs once');

        const bools = {
            boolOne: false,
            boolTwo: false,
            boolThree: false
        };

        const events = {
            clickEvent:
                function(event) {
                    //do stuff
                },
            keyEvent:
                function(event) {
                    //do other stuff
                }
        };

        return {
            bools: bools,
            events: events
        }
    };

    if (!isVarsInitOnce) {
        isVarsInitOnce = true;
        let vars = initVariables();

        var booleanObject = vars.bools;
        var eventObject = vars.events;
    }

    console.log('objects: ', booleanObject, eventObject);

    //attempting to access initialised variable after function is executed more than once will cause an error.
    //this is because the booleanObject is now undefined.
    booleanObject.boolOne = true;
}

//runs twice
for (let i=0; i<2; i++) {
    executesMoreThanOnce();
}

我用来控制 initVariables() 执行的方法是一个全局布尔变量,isVarsInitOnce 它在初始化变量和设置对象以在 [=16] 中使用是有效的=] 函数一次。

for 循环中调用函数的第一个实例中可以访问对象,但是当试图在次要实例中访问对象时 undefined该函数在 for 循环中被调用。

这在控制台输出中非常清楚地表示:

runs once
test.js:38 objects:  {boolOne: false, boolTwo: false, boolThree: false} {clickEvent: ƒ, keyEvent: ƒ}
test.js:38 objects:  undefined undefined //<--- (function called 2nd time)
test.js:42 Uncaught TypeError: Cannot set property 'boolOne' of undefined
    at executesMoreThanOnce (test.js:42)
    at test.js:47

我不确定为什么会这样。

谁能帮我理解为什么这不能正常工作?

对于我的案例,有没有人有更好的减少全局变量的建议?

非常感谢。

首先,booleanObject 和 eventObject 都需要在 if 语句之外声明才能在范围内。 其次,如果您需要 运行 executesMoreThanOnce 不止一次,则需要在 for 循环

中将 isVarsInitOnce 设置为 false
           let  isVarsInitOnce = false;
           
function executesMoreThanOnce() {
    const initVariables = function() {
        console.log('runs once');

        const bools = {
            boolOne: false,
            boolTwo: false,
            boolThree: false
        };

        const events = {
            clickEvent:
                function(event) {
                    //do stuff
                },
            keyEvent:
                function(event) {
                    //do other stuff
                }
        };

        return {
            bools: bools,
            events: events
        }
    };
      let booleanObject ; // declaring booleanObject outside the if statement
        let eventObject ; // declaring eventObject outside the if statement
    if (!isVarsInitOnce) {
        isVarsInitOnce = true;
        let vars = initVariables();

        booleanObject = vars.bools;
        eventObject = vars.events;
    }

    console.log('objects: ', booleanObject, eventObject);

    //attempting to access initialised variable after function is executed more than once will cause an error.
    //this is because the booleanObject is now undefined.
    booleanObject.boolOne = true;
}

//runs 5 times
for (let i=0; i<5; i++) {// Will execute 5 times
    executesMoreThanOnce();
    isVarsInitOnce = false;
}

编辑

抱歉,我没有完全理解您的要求。查看以下内容:

JavaScript

    // This will be the object that holds your variables
     let vars;
       
       function executesMoreThanOnce(obj) {
           const initVariables = function(obj) {
              
               console.log('runs once and obj = ' + obj);
               if (obj == undefined) {
                
                console.log('initialize once');
                    const bools = {
                        boolOne: false,
                        boolTwo: false,
                        boolThree: false
                    };
            
                    const events = {
                        clickEvent:
                            function(event) {
                                //do stuff
                            },
                        keyEvent:
                            function(event) {
                                //do other stuff
                            }
                    };
                   
                   return (function(){
                        return {bools: bools,
                                events: events};
                    })();
                }
                // If vars object, "which we passed as argument", is initialized once before just return it
                return vars;
               
           };

           vars = initVariables(vars);
               
           let    booleanObject = vars.bools;
           let    eventObject = vars.events;
               
           
       
           console.log('objects: ', booleanObject, eventObject);
       
           //attempting to access initialised variable after function is executed more than once will cause an error.
           //this is because the booleanObject is now undefined.
           // Yes you can now access the variables
           booleanObject.boolOne = true;
       }
       
       //runs 5 times
       for (let i=0; i<5; i++) {// Will execute 5 times
            // Removed the bool flag and now passing the vars object as argument
           executesMoreThanOnce(vars);
          
           //executesMoreThanOnce();
           //isVarsInitOnce = false;
       }

Check it out on Fiddler

我看了一些关于 JS 范围和闭包的阅读材料,我想我已经找到了解决我原来问题中分解问题的方法。

Aber Abou-Rahma 给出的答案并没有解决我的问题,因为每个 executesMoreThanOnce().

的函数调用都会重置变量

这不是我想要的,因为我只希望在函数被调用的第一个实例中初始设置变量(因为在我的实际项目中 executesMoreThanOnce() 函数本质上代表一个点击事件要求数据在事件重新触发时保持不变的回调)。

我的解决方案使用立即调用函数表达式 (IIFE)。在 IIFE 范围内局部初始化变量,并在其返回的 get 方法中将变量释放到全局范围:

test.js

const TEST = (function() {
    let booleans = {
        boolOne: false,
        boolTwo: false,
        boolThree: false
    };

    let events = {
        clickEvent:
            function(event) {
                //do stuff
            },
        keyEvent:
            function(event) {
                //do other stuff
            }
    };

    return {
        executesMoreThanOnce: function(booleans, events, index) {
            booleanObject = booleans;
            eventsObject = events;

            if (i == 2) {
                booleanObject.boolTwo = true;
            }
            else if (i == 4) {
                booleanObject.boolOne = true;
                booleanObject.boolTwo = false;
            }

            console.log('booleanObject: ', booleanObject);
        },
        get variables() {
            return {
                booleans,
                events
            }
        }
    };
}());

for (var i=0; i<5; i++) {
    TEST.executesMoreThanOnce(TEST.variables.booleans, TEST.variables.events, i);
}

您现在可以在控制台中看到 TEST.executesMoreThanOnce() 函数开始使用 TEST IIFE 函数中本地定义的初始变量:

i = 0 | booleanObject:  {boolOne: false, boolTwo: false, boolThree: false}
i = 1 | booleanObject:  {boolOne: false, boolTwo: false, boolThree: false}
i = 2 | booleanObject:  {boolOne: false, boolTwo: true, boolThree: false}
i = 3 | booleanObject:  {boolOne: false, boolTwo: true, boolThree: false}
i = 4 | booleanObject:  {boolOne: true, boolTwo: false, boolThree: false}

我们现在还可以看到,一旦 i 值在 TEST.executesMoreThanOnce() 函数中满足某些条件,布尔值就会开始切换,但更重要的是,这些变化在函数调用之间持续存在。

我仍然不确定我是否完全理解 IIFE 下的抽象概念。但是我可以在代码中清楚地看到哪些变量属于它们在哪些函数中使用的范围(提高可读性)。这首先是我的目标。

在我开始尝试将其实施到我的项目之前,如果有人想纠正我的错误,非常欢迎您。