JavaScript中eval的替代方法有哪些?

22
我有一小段代码,看起来像这样:

I have a little bit of code that looks just like this:

function StrippedExample(i1, i2, i3, i4, i5, i6, i7, i8) {
    this.i = [];
    for (var i=1,j=0 ;i<9;i++) {
        var k = eval("i"+i);
        if (k > 0) {
            this.i[j++] = k;
        }
    }
}

FireBug分析器指出,第二长的函数是eval(),运行时长最高可达近6%。

大家都说eval很“邪恶”(意为不好的),也很慢(正如我所发现的),但我真的无法做其他事情——服务器只是从数据库中提取数据并推送到浏览器。

我有什么替代方案吗?我可以在服务器上执行与此处相同的操作,但这只是将负担转移到了更高层次。我不能更改数据库布局,因为所有东西都连接到这8个变量,并且是一项巨大的工程。


3
希望这能帮助人们明白,你从来不必使用“eval”。 - ChaosPandion
11个回答

18
function StrippedExample(i1, i2, i3, i4, i5, i6, i7, i8) {
    var args = [i1, i2, i3, i4, i5, i6, i7, i8]; // put values in an array
    this.i = [];
    for (var i=0,j=0 ;i<8;i++) { // now i goes from 0-7 also
        var k = args[i]; // get values out
        if (k > 0) {
            this.i[j++] = k;
        }
    }
}

上面的代码可以进一步简化,我只是做了最小的更改来摆脱 eval。例如,你可以摆脱 j

function StrippedExample(i1, i2, i3, i4, i5, i6, i7, i8) {
    var args = [i1, i2, i3, i4, i5, i6, i7, i8];
    this.i = [];
    for (var i = 0; i < args.length; i++) {
        var k = args[i];
        if (k > 0) { this.i.push(k); }
    }
}

是等价的。或者,为了使用内置的arguments对象(避免在两个地方使用参数列表):

function StrippedExample(i1, i2, i3, i4, i5, i6, i7, i8) {
    this.i = [];
    for (var i = 1; i < arguments.length; i++) {
        var k = arguments[i];
        if (k > 0) { this.i.push(k); }
    }
}

即使你没有对列表进行过滤,也不要像这样做this.i = arguments,因为arguments不是一个真正的数组;它有一个你不需要的属性,并且缺少一些在i中可能需要的数组方法。正如其他人指出的那样,如果你想快速将arguments对象转换为数组,可以使用以下表达式:

Array.prototype.slice.call(arguments)

你可以使用这个代替上面的var args = [i1, i2 ...代码行。


1
此代码目前是错误的,因为它使用了从1开始索引的方式,而数组是从0开始索引的。它也明显比必要的复杂,因为你可以直接用内置的arguments数组替换args,而不需要自己定义它。 - Brian Campbell
1
现在你错过了原始代码中的 if (k > 0) 部分。 - Brian Campbell
1
不将 this.i = arguments 的另一个原因是因为 arguments 也缺少您可能想要在 i 中的属性,因为它不是一个数组。 - Matthew Crumley
1
一个更好的例子是 this.i = Array.prototype.slice.call(arguments) - Eli Grey
第一个参数不应该被删除。只有在参数等于0时,它们才应该被删除。 - graham.reeds
显示剩余5条评论

12

评估替代方案:

exp = '1 + 1'
x = Function('return ' + exp)()
console.log(x)

1
+1 针对 eval() 的替代方案的建议。不幸的是,它在 Firefox 中像 eval 一样被 CSP(default-src 'self')拒绝。 - Code4R7

8

你只需从函数的8个参数中创建一个数组,删除小于或等于零的参数。

以下代码是等效的,并且适用于任意数量的参数:

function StrippedExample() {
  var args = [];

  for (var i = 0; i < arguments.length; i++) {
    if (arguments[i] > 0) {
      args.push(arguments[i]);
    }
  }
  //...
}

不要忘记 if (k > 0) 部分。 - JPot

5
  1. 使用一个参数(数组)调用该函数
  2. 使用arguments对象

4

与单独传递参数的方法相比,您可以将数组作为参数传递给函数:

StrippedExample([3, 1, 4, 1, 5, 9, 2, 6])

那么你的代码将是这样的:
function StrippedExample(inArray) {
    this.i = [];
    for (var i=0,j=0 ;i<inArray.length;i++) {
        var k = inArray[i];
        if (k > 0) {
            this.i[j++] = k;
        }
    }
}

如果您确实需要传递单独的参数,可以使用您的arguments数组来访问它们,这是一个像数组一样的对象(尽管它并不是真正的数组;并非所有的Array方法都适用于它),它公开了传递给函数的所有参数; 在这种情况下,甚至不需要声明它们,但对于代码用户来说,包含指示您期望哪些类型的参数的注释是很好的形式:
function StrippedExample(/*i1, i2, i3, i4, i5, i6, i7, i8*/) {
    this.i = [];
    for (var i=0,j=0 ;i<arguments.length;i++) {
        var k = arguments[i];
        if (k > 0) {
            this.i[j++] = k;
        }
    }
}

如果你保证只有8个元素,那么你可以在inArray.lengtharguments.length的位置使用8; 我在我的示例中选择使用更通用的版本,以便对你有所帮助。

1
function StrippedExample() {

    this.i = [];
    for (var i=1,j=0 ;i<arguments.length;i++) {
        var k = arguments[i];
        if (k > 0) {
            this.i[j++] = k;
        }
    }
}

1
简短回答:
StrippedExample=(...a)=>a.filter(i=>i>0);

完全不需要使用eval来处理参数。

  1. 初始代码和大多数提议的解决方案都没有按传统方式返回结果。

1

这段代码应该使用每个JavaScript函数都可以访问的arguments数组。

并不是因为eval邪恶的(它在Lisp中,所以它肯定是好的),它只是一个不良编程设计的迹象——你需要让某些东西工作,于是你被迫使用了它。对我而言,它大喊着一句话:“作者放弃了良好的编程设计,只是找到了一个可行的解决方案。”。


我不是很熟悉JavaScript的所有细节。我可以通过一些技巧来完成任务,但正如在这里展示的那样,并不总是最好的方式。 - graham.reeds
那么,这些复杂的解决方法中没有一个是黑客技巧,但这个方便的一行函数就是黑客技巧? - johny why

0
鉴于变量数目固定,您可以手动构建一个数组并循环遍历它。但如果参数数量可变,则将变量传递到函数的一种方法是将它们作为数组传递。
var args = Array.prototype.slice.call(arguments.callee.caller.arguments);

然后你的函数看起来会像这样:

function StrippedExample() {
    var args = Array.prototype.slice.call(arguments.callee.caller.arguments);
    for(var i in args) {
        if (args[i] > 0) {
            this.i[j++] = args[i];
        }
    }
}

2
在迭代数组时,不应使用 for..in。简单的 for 循环更好。 - JPot

0

你也可以使用setTimeout运行字符串表达式。它的工作方式与Function对象相同。

let a=10;
window.a=100;
window.b=1;
setTimeout("let c=1000;console.log(a,b,c)");//10,1,1000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接