Javascript eval() 行为奇怪

Javascript eval() acting strange

提问人:Jonathan Plumb 提问时间:7/22/2022 更新时间:7/23/2022 访问量:74

问:

我正在创建自己的消息框(正常的“确定”对话框和“是/否”对话框),这些对话框可以在我的网站上弹出。当用户单击“确定”或“是/否”时,将调用使用 eval() 执行的回调。OK 回调工作正常,但我似乎无法让 Yes/No 回调工作。

我将首先显示 OK messageBox 代码,然后显示 yesNoBox 代码。

function messageBox(msg, onClose = null) {
    var id = sessionStorage.getItem('messageIndex');
    if (id === null || id === 'undefined') id = "0";
    sessionStorage.setItem('messageIndex', parseInt(id) + 1);
    sessionStorage.setItem('mb' + id, msg);
    sessionStorage.setItem('mb' + id + "_onClose", onClose);
    
    var code = localStorage.getItem('mb_ok');
    if (code === null || code === 'undefined') request('GET ui/mb_ok.html?id=' + id, 'messageBoxResponse');
    else {
        var start = code.indexOf('mb_ok') + 'mb_ok'.length;
        var end = code.indexOf('"', start);
        var i = code.substring(start, end);
        code = code.replace('mb_ok' + i, 'mb_ok' + id);
        messageBoxResponse(code);
    }
}

request('GET ui/mb_ok.html?id=' + id', 'messageBoxResponse') 使用 WebSockets 检索 messageBox HTML。服务器返回具有唯一 messageBox ID 的 HTML 并调用 messageBoxResponse()。

function messageBoxResponse(response) {
    var start = response.indexOf('mb_ok') + 'mb_ok'.length;
    var end = response.indexOf('"', start);
    var i = response.substring(start, end);
    var id = parseInt(i);
    var msg = sessionStorage.getItem('mb' + id);
    $('#stage').append(response);
    $('#mb_ok' + id + '_body').html(msg);
    var onClose = sessionStorage.getItem('mb' + id + '_onClose');
    if (onClose !== null || onClose !== 'undefined') {
        $('#mb_ok' + id).on('destroyed', eval(onClose));
    }
    document.activeElement.blur();
}

测试代码:

messageBox("This is my message box", () => { alert("BOX CLOSED!"); });

Message Box

当我点击“确定”时,我得到:

Message Box closed alert

YesNoBox 代码几乎相同,但同时具有 onYes 和 onNo 回调。

function yesNoBox(msg, onYes, onNo) {
    var id = sessionStorage.getItem('mb_yesno_index');
    if (id === null || id === 'undefined') id = "0";
    sessionStorage.setItem('mb_yesno_index', parseInt(id) + 1);
    sessionStorage.setItem('mb_yesno' + id, msg);
    sessionStorage.setItem('mb_yesno' + id + '_onYes', onYes);
    sessionStorage.setItem('mb_yesno' + id + '_onNo', onNo);
    
    var code = localStorage.getItem('mb_yesno');
    if (code === null || code === 'undefined') request('GET ui/mb_yesno.html?id=' + id, 'yesNoBoxResponse');
    else {
        var start = code.indexOf('mb_yesno') + 'mb_yesno'.length;
        var end = code.indexOf('"', start);
        var i = code.substring(start, end);
        code = code.replace('mb_yesno' + i, 'mb_yesno' + id);
        yesNoBoxResponse(code);
    }
}

yesNoBoxResponse() 函数几乎相同,但增加了检查以确定是否选择了 yes 或 no:

function yesNoBoxResponse(response) {
    var start = response.indexOf('mb_yesno') + 'mb_yesno'.length;
    var end = response.indexOf('"', start);
    var i = response.substring(start, end);
    var id = parseInt(i);
    var msg = sessionStorage.getItem('mb_yesno' + id);
    $('#stage').append(response);
    $('#mb_yesno' + id + '_body').html(msg);
    
    $('#mb_yesno' + id).on('destroyed', () => {
        var answer = sessionStorage.getItem('mb_yesno' + id + '_answer');
        if (answer === null || answer === 'undefined') {
            errorBox('INTERNAL SERVER ERROR: dev.js@' + new Error().lineNumber);
        } else {
            var onYes = sessionStorage.getItem('mb_yesno' + id + '_onYes');
            var onNo = sessionStorage.getItem('mb_yesno' + id + '_onNo');
            alert('onYes = ' + onYes + '\n\nonNo = ' + onNo);
            if (onYes === null || onYes === 'undefined') onYes = 'errorBox("SERVER ERROR");';
            if (onNo === null || onNo === 'undefined') onNo = 'errorBox("SERVER ERROR");';
            //alert("onYes = " + onYes + "\nonNo = " + onNo);
            if (answer === 'YES') {
                eval(onYes);
            } else {
                eval(onNo);
            }
        }
    });
    document.activeElement.blur();
}

现在问题来了。eval(onYes) 和 eval(onNo) 将不起作用,即使代码正确。YesNo 框确实按预期关闭,但 eval() 都没有触发。

这是我打电话时看到的内容

yesNoBox("This is my YESNO box.", () => { alert("YES CLICKED"); }, () => { alert("NO CLICKED"); });

(注意:我添加了alert('onYes = ' + onYes + '\n\nonNo = ' + onNo);只是为了表明onYes和onNo确实保留了正确的代码)

YesNo Box

这是该警报,在变量中显示正确的代码:

enter image description here

但是 Yes 和 No 实际上都没有用 eval() 触发。调试器不会显示任何问题。一切运行良好,但 YesNo 框只是关闭而不触发任何回调。

对不起,这篇文章很长,但我想确保我在这里涵盖了我的基础。有什么想法吗?

JavaScript jQuery 回调 评估

评论

0赞 Diego D 7/22/2022
在传递给 eval() 之前,您是否有字符串 onYes 和 onNo?你看到了什么?更重要的是,如果回答 === 'YES',他们是否通过了条件?
0赞 Jonathan Plumb 7/22/2022
这就是最后一个警报图像显示的内容,“onYes”包含“() => { alert(”YES CLICKED“); }”,而“onN”包含“() => { alert(”NO CLICKED“); }'。然后“if (answer === 'YES') { eval(onYes);}else { eval(onNo);
0赞 Diego D 7/22/2022
但这只是一个定义。这不是调用..这是一个调用:(()=>{/*logic*/})();
2赞 Bergi 7/22/2022
"调用使用 eval() 执行的回调。- 请不要那样做!回调是函数,只需调用它们 like 或 即可。onYes()onClose()
1赞 Bergi 7/22/2022
@JonathanPlumb 这更没有意义。您可以将函数保留在局部变量中,并在 websocket 返回响应时调用它们。sessionStorage 项本质上是一个纯字符串的全局变量 - 不要将其用于函数!您没有在多个页面(选项卡)之间进行通信,是吗?

答:

0赞 Jonathan Plumb 7/22/2022 #1

多亏了评论,我才能够解决问题。我把代码改成了:

if (answer === 'YES') {
    eval(onYes)();
} else {
    eval(onNo)();
}

在“eval(...)”之后添加“()”解决了这个问题。

评论

1赞 epascarello 7/22/2022
仅供参考:eval 是错误的答案
3赞 epascarello 7/22/2022 #2

只需使用带有对象的数组即可。您可以使用 id 来获取对象,并且在执行函数时不需要 eval。

var myCallbacks = [];

function yesNoBox(msg, onYes, onNo) {
  const currentId = myCallbacks.length;
  myCallbacks.push({
    onYes,
    onNo
  });

  //this is just to show you, I know it is not the same, faking a response
  window.setTimeout(() => yesNoBoxResponse(`mb_yesno${currentId}"`), Math.ceil(4000 * Math.random()));

}

function yesNoBoxResponse(response) {
  var start = response.indexOf('mb_yesno') + 'mb_yesno'.length;
  var end = response.indexOf('"', start);
  var i = response.substring(start, end);
  var id = parseInt(i);
  var data = myCallbacks[id];
  data.onYes();
  data.onNo();
  myCallbacks[id] = undefined;
}


yesNoBox('xxxxxx', ()=>console.log('yes 1'), ()=>console.log('no 1'));
yesNoBox('xxxxxx', ()=>console.log('yes 2'), ()=>console.log('no 2'));
yesNoBox('xxxxxx', ()=>console.log('yes 3'), ()=>console.log('no 3'));

使用对象而不是数组

var myCallbacks = {};
let currentKey = 0;

function yesNoBox(msg, onYes, onNo) {
  const currentId = currentKey++;
  myCallbacks[currentId] = {
    onYes,
    onNo
  };

  //this is just to show you, I know it is not the same, faking a response
  window.setTimeout(() => yesNoBoxResponse(`mb_yesno${currentId}"`), Math.ceil(4000 * Math.random()));

}

function yesNoBoxResponse(response) {
  var start = response.indexOf('mb_yesno') + 'mb_yesno'.length;
  var end = response.indexOf('"', start);
  var id = response.substring(start, end);
  var data = myCallbacks[id];
  data.onYes();
  data.onNo();
  delete data[id];
}


yesNoBox('xxxxxx', ()=>console.log('yes 1'), ()=>console.log('no 1'));
yesNoBox('xxxxxx', ()=>console.log('yes 2'), ()=>console.log('no 2'));
yesNoBox('xxxxxx', ()=>console.log('yes 3'), ()=>console.log('no 3'));

评论

0赞 Jonathan Plumb 7/22/2022
哈哈,我实际上正在更改我的代码以准确反映这一点。感谢大家的评论。我希望它以这种方式工作,因为我真的不喜欢 eval()。
0赞 Bergi 7/22/2022
这是一个巨大的进步!不过,它仍然(像原始代码一样)存在内存泄漏问题,因为当弹出窗口已经关闭时,该问题永远不会被清除。您可能希望使用对象或数组代替数组,使用 id(包括前缀,因此不需要任何解析)作为键,然后在使用它后使用回调数据。myCallbacksMapmb_yesnodelete
0赞 Jonathan Plumb 7/22/2022 #3

感谢评论,我已经解决了我的问题并删除了 eval()。

我创建了自己的 Dictionary 类,其功能与 sessionStorage 几乎相同,但现在都是本地的。

函数 messageBox() 现在是

function messageBox(msg, onClose = null) {
    var id = sessionStorage.getItem('messageIndex');
    if (id === null || id === 'undefined') id = "0";
    sessionStorage.setItem('messageIndex', parseInt(id) + 1);
    
    mb_dictionary.setItem('mb' + id + '_msg', msg);
    mb_dictionary.setItem('mb' + id + '_onClose', onClose);
    
    request('GET ui/mb_ok.html?id=' + id, 'messageBoxResponse');
}

函数 messageBoxResponse() 现在是

function messageBoxResponse(response) {
    var start = response.indexOf('mb_ok') + 'mb_ok'.length;
    var end = response.indexOf('"', start);
    var i = response.substring(start, end);
    var id = parseInt(i);
    var msg = mb_dictionary.removeItem('mb' + id + '_msg');
    $('#stage').append(response);
    $('#mb_ok' + id + '_body').html(msg);
    
    var onClose = mb_dictionary.removeItem('mb' + id + '_onClose');
    if (onClose !== null && onClose !== 'undefined') {
        $('#mb_ok' + id).on('destroyed', onClose);
    }
    
    document.activeElement.blur();
}

类似地,函数 yesNoBox() 现在是:

function yesNoBox(msg, onYes, onNo) {
    var id = sessionStorage.getItem('messageIndex');
    if (id === null || id === 'undefined') id = "0";
    sessionStorage.setItem('messageIndex', parseInt(id) + 1);
    
    mb_dictionary.setItem('mb' + id + '_msg', msg);
    mb_dictionary.setItem('mb' + id + '_onYes', onYes);
    mb_dictionary.setItem('mb' + id + '_onNo', onNo);
    
    request('GET ui/mb_yesno.html?id=' + id, 'yesNoBoxResponse');
}

函数 yesNoBoxResponse() 现在是:

function yesNoBoxResponse(response) {
    var start = response.indexOf('mb_yesno') + 'mb_yesno'.length;
    var end = response.indexOf('"', start);
    var i = response.substring(start, end);
    var id = parseInt(i);
    var msg = mb_dictionary.removeItem('mb' + id + '_msg');
    
    $('#stage').append(response);
    $('#mb_yesno' + id + '_body').html(msg);
    
    var onYes = mb_dictionary.removeItem('mb' + id + '_onYes');
    var onNo = mb_dictionary.removeItem('mb' + id + '_onNo');
    $('#mb_yesno' + id).on('destroyed', () => {
        var answer = mb_dictionary.removeItem('mb' + id + '_answer');
        if (answer === 'YES' && onYes !== null && onYes !== 'undefined') onYes();
        else if (answer === 'NO' && onNo !== null && onNo !== 'undefined') onNo();
    });
    
    document.activeElement.blur();
}

现在一切正常!