为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

一只名叫tom的猫 2023-09-21 10:48:56
我正在尝试减少 Chrome 扩展中的全局变量,以减少 JavaScript 中的“意大利面”或歧义。我试图通过一个 init 函数来尝试此操作,该函数在 mousedown eventListener 回调函数中声明这些全局变量,该函数可能会被多次触发。这样我就可以将这些变量+事件作为此类回调的参数传递给其他 eventListener 回调(即 mouseup 回调)。我已将问题分解为一个单独的文件:索引.html<!doctype html><html><head>  <meta charset="utf-8"></head><body>  <script src="test.js"></script></body></html>测试.jsisVarsInitOnce = 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 twicefor (let i=0; i<2; i++) {    executesMoreThanOnce();}我用来控制执行的方法initVariables()是一个全局布尔变量,isVarsInitOnce它可以有效地初始化变量并设置executesMoreThanOnce()一次在函数中使用的对象。可以在循环中调用该函数的第一个实例中访问这些for对象,但是undefined当尝试在循环中调用该函数的第二个实例中访问这些对象时,就会访问这些对象for。这在控制台输出中非常清楚地表示出来:runs oncetest.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我不确定为什么会发生这种情况。谁能帮助我理解为什么这不能正常工作?对于我的情况,有人有更好的建议来减少全局变量吗?非常感谢。
查看完整描述

2 回答

?
红糖糍粑

TA贡献1815条经验 获得超6个赞

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

       }


查看完整回答
反对 回复 2023-09-21
?
肥皂起泡泡

TA贡献1829条经验 获得超6个赞

我看过一些关于 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);

}

您现在可以在控制台中看到该TEST.executesMoreThanOnce()函数开始使用 IIFE 函数中本地定义的初始变量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 背后的抽象。但我可以在代码中清楚地看到哪些变量属于它们所使用的函数的范围(提高可读性)。这首先是我的目标。


如果有人想纠正我的一些误解,那么在我开始尝试将其应用到我的项目之前,我们非常欢迎您的参与。


查看完整回答
反对 回复 2023-09-21
  • 2 回答
  • 0 关注
  • 64 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信