0

我正在尝试减少 Chrome 扩展程序中的全局变量,以减少 JavaScript 中的“意大利面”或歧义。

我试图通过使用一个 init 函数来尝试此操作,该函数在可能多次触发的 mousedown eventListener 回调函数中声明这些否则为全局变量。

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

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

索引.html

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

测试.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它在初始化变量和设置对象以在executesMoreThanOnce()函数中使用一次时是有效的。

在循环中调用函数的第一个实例中可以访问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

我不确定为什么会发生这种情况。

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

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

非常感谢。

4

2 回答 2

1

首先 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;
       }

在 Fiddler 上查看

于 2020-12-01T06:02:54.553 回答
0

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

Aber Abou-Rahma 给出的答案并没有解决我的问题,因为每个函数调用都会重置变量executesMoreThanOnce()

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

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

测试.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);
}

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

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 下的抽象。但我可以在代码中清楚地看到哪些变量属于它们所使用的函数的范围(提高可读性)。这是我最初的目标。

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

于 2020-12-02T00:48:53.970 回答