Javascript 避免使用eval()动态构建事件处理程序

Javascript 避免使用eval()动态构建事件处理程序,javascript,dynamic,event-handling,eval,Javascript,Dynamic,Event Handling,Eval,我正在努力管理javascript中动态构建的事件处理程序 在一些地方,我构建表单或控件,在这些表单或控件中需要处理特定事件(主要是鼠标移动、鼠标移出、单击) 诀窍在于,在很多情况下,事件处理程序本身需要合并由生成的数据,或是传递到构建表单或控件的函数中的数据 因此,我一直在使用“eval()”来构造事件并合并适当的数据,这在某种程度上效果不错 问题是我不断看到/听到诸如“您不应该使用eval()!”之类的东西,以及一些越来越丑陋的实现,其中我的动态构建的事件处理程序需要动态构建其他事件处理程序

我正在努力管理javascript中动态构建的事件处理程序

在一些地方,我构建表单或控件,在这些表单或控件中需要处理特定事件(主要是鼠标移动、鼠标移出、单击)

诀窍在于,在很多情况下,事件处理程序本身需要合并由生成的数据,或是传递到构建表单或控件的函数中的数据

因此,我一直在使用“eval()”来构造事件并合并适当的数据,这在某种程度上效果不错

问题是我不断看到/听到诸如“您不应该使用eval()!”之类的东西,以及一些越来越丑陋的实现,其中我的动态构建的事件处理程序需要动态构建其他事件处理程序,而嵌套的eval非常迟钝(说得委婉一点)

所以我在这里,问是否有人可以告诉我更好的方法(请只使用原生javascript,我没有实现任何第三方库!)

下面是一个简单的例子来说明我所说的:

function CreateInput(controlName,type,activeStyle,dormantStyle,whenClicked)
{
    var inp = document.createElement('input');
    inp.id = controlName;
    inp.type = type;
    inp.style.cssText = dormantStyle;
    eval("inp.onfocus = function() { this.style.cssText = '" + activeStyle + "'; }");
    eval("inp.onblur = function() { this.style.cssText = '" + dormantStyle + "'; }");
    eval("inp.onclick = function() { " + whenClicked + "; }");
    return inp;
}
显然,这个函数可以让我轻松地创建许多不同的输入标记,并指定许多独特的属性和事件操作,每个都只需一个函数调用。同样,这是一个极其简化的示例,只是为了演示我所说的,在我目前参与的项目中,在某些情况下,事件可以包含几十行,它们甚至可以基于传递的参数或其他动态生成的数据进行动态ajax调用。在更极端的情况下,我构造表,表中的单个行/列/单元格可能需要根据处理程序或处理程序的处理程序动态生成的内容来处理事件

最初,我构建了类似上述的函数:

function CreateInput(controlName,type,activeStyle,dormantStyle,whenClicked)
{
    var inp = document.createElement('input');
    inp.id = controlName;
    inp.type = type;
    inp.style.cssText = dormantStyle;
    inp.onfocus = function() { this.style.cssText = activeStyle; };
    inp.onblur = function() { this.style.cssText = dormantStyle; };
    eval("inp.onclick = function() { " + whenClicked + "; }");
    return inp;
}
…但我发现,无论最后为“activeStyle”和“dormantStyle”分配的值是什么,它们都成为所有由此创建的处理程序所使用的值(例如,不是每个处理程序都保留自己独特的样式集)。这就是为什么我在创建函数时使用eval()来“锁定”变量的值,但这让我陷入了如下噩梦:

(这是我目前正在处理的一个动态构建的事件处理程序的示例,它使用嵌套的eval()函数):

eval(“input.onkeyup=function(){”+
InputParse(这是,'ucwords')+
“var tId=”+myName+This.nodeName+“SearchTable”+uidNo+”+
“var table=document.getElementById(tId);”+
“如果(this.value.length>2){”+
var val=(this.value.indexOf(',')>=0)?this.value.substr(0,this.value.indexOf(',')):this.value+
“var search=Global.LoadData('?fn=citySearch&limit=3&value='+encodeURI(val))+
“如果(表){”+
“while(table.rows.length>0){table.deleteRow(0);}”+
“table.style.display='block'+
“}否则{”+
“table=document.createElement('table')+
“table.id=tId;”+
ApplyStyleString(“+baseStyle+”;位置=绝对;顶部=20px;左侧=0px;显示=块;边框=1px纯黑色;背景色=rgba(224,0.90);zIndex=1000;”,表格)+
var div=document.getElementById(“+divName+”)+
“if(div){div.appendChild(table);}”+
"} " +
“如果(search.rowCount()>0){”+

对于(var i=0;i您不需要
eval
来“锁定”一个值

从发布的代码中不清楚为什么在
CreateInput
返回后会看到值发生变化。如果
CreateInput
实现了一个循环,那么我希望使用分配给
activeStyle
DormentStyle
的最后一个值。但即使从循环中调用
CreateInput
,也不会导致与评论者相反,你描述的不当行为

无论如何,这种陈旧数据的解决方案是使用闭包。JavaScript局部变量都绑定到函数调用范围,无论它们是在函数内部还是在循环中声明的。因此,添加函数调用以强制创建新变量

function CreateInput(controlName,type,activeStyle,dormantStyle,whenClicked)
{
    while ( something ) {
        activeStyle += "blah"; // modify local vars
        function ( activeStyle, dormantStyle ) { // make copies of local vars
            var inp = document.createElement('input');
            inp.id = controlName;
            inp.type = type;
            inp.style.cssText = dormantStyle;
            inp.onfocus = function() { this.style.cssText = activeStyle; };
            inp.onblur = function() { this.style.cssText = dormantStyle; };
            inp.onclick = whenClicked;
        }( activeStyle, dormantStyle ); // specify values for copies
    }
    return inp;
}

您不需要
eval
来“锁定”值

从发布的代码中不清楚为什么在
CreateInput
返回后会看到值发生变化。如果
CreateInput
实现了一个循环,那么我希望使用分配给
activeStyle
DormentStyle
的最后一个值。但即使从循环中调用
CreateInput
,也不会导致与评论者相反,你描述的不当行为

无论如何,这种陈旧数据的解决方案是使用闭包。JavaScript局部变量都绑定到函数调用范围,无论它们是在函数内部还是在循环中声明的。因此,添加函数调用以强制创建新变量

function CreateInput(controlName,type,activeStyle,dormantStyle,whenClicked)
{
    while ( something ) {
        activeStyle += "blah"; // modify local vars
        function ( activeStyle, dormantStyle ) { // make copies of local vars
            var inp = document.createElement('input');
            inp.id = controlName;
            inp.type = type;
            inp.style.cssText = dormantStyle;
            inp.onfocus = function() { this.style.cssText = activeStyle; };
            inp.onblur = function() { this.style.cssText = dormantStyle; };
            inp.onclick = whenClicked;
        }( activeStyle, dormantStyle ); // specify values for copies
    }
    return inp;
}

这也是为什么你应该永远不要使用
eval
(如果你正在“烘焙”的那些值中包含引号呢?哎呀。)更一般地说,试着找出为什么正确的方法不起作用,而不是用错误的方法提交。:)

另外,在*
属性上指定
不是一个好主意;它们的伸缩性不太好。新的热点是使用
元素。addEventListener
,它允许对同一事件使用多个处理程序。(对于较旧的IE,您需要
attachEvent
。这种IE胡说八道是我们开始使用像jQuery这样的库的主要原因。)


您粘贴的代码使用闭包,应该可以正常工作。您没有包括的部分是,您一定是在l
var callbacks = [];
for (var i = 0; i < 10; i++) {
    callbacks.push(function() { alert(i) });
}

for (var index in callbacks) {
    callbacks[index]();
}
for (var i = 0; i < 10; i++) {
    (function(i) {
        callbacks.push(function() { alert(i) });
    })(i);
}
function make_function(i) {
    return function() { alert(i) };
}

// ...

for (var i = 0; i < 10; i++) {
    callbacks.push(make_function(i));
}
function create_input(id, type, active_class, onclick) {
    var inp = document.createElement('input');
    inp.id = id;
    inp.type = type;
    inp.addEventListener('focus', function() {
        this.className = active_class;
    });
    inp.addEventListener('blur', function() {
        this.className = '';
    });
    inp.addEventListener('click', onclick);

    return inp;
}

// Called as:
var textbox = create_input('unique-id', 'text', 'focused', function() { alert("hi!") });
function create_input(id, type, active_class, onclick) {
    var inp = $('<input>', { id: id, type: type });
    inp.on('focus', function() {
        $(this).addClass(active_class);
    });
    inp.on('blur', function() {
        $(this).removeClass(active_class);
    });

    inp.on('click', onclick);

    return inp;
}