整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

在JavaScript中尽量不使用eval

在修改一个问题的时候,发现在竟然用eval计算数组的和,不知道当时为什么会这样写(有点不过脑子),不过eval尽量不要使用,在MDN有2个不建议使用eval的建议:

  1. 安全性,eval使用与调用者相同的权限执行代码,如跨站脚本攻击(XSS),恶意脚本注入到页面中,从而控制用户的浏览器.
  2. 性能,eval必须调用JavaScript的解释器,在由解释器转为机器码,任意一个eval的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值.

如果要使用evel,可以使用Function进行替代,在MDN说Function比eval快,在后面的测试中,Function并没有比eval快很多.

示例代码

function generateRandomArray(len) {
    let arr = [];
    for (let i = 0; i < len; i++) {
        let value = Math.floor(Math.random() * len * 10);
        arr.push(value);
    }
    return arr;
}

function test() {
    let sumArr = [];
    let randomArray = generateRandomArray(16);
    for (var i = 0, len = randomArray.length; i < len; i++) {
        //处理其他的逻辑
        sumArr.push(randomArray[i]);
    }
    let expression = sumArr.join('+');
    let sum = eval(expression); //使用eval计算 '1+2'表达式
    console.log(expression);
    console.log(sum);
}

看一下结果:

JavaScript使用eval计算表达式

先说说test函数中有哪些问题:

1) 使用eval计算表达式

2) 使用sumArr数组,多增加内存的使用,计算数组内值的和,没必要增加一个数组.

不使用evel,可以使用那些方式

1) 最直接的方式,直接在for循环中进行计算

2) 使用JavaScript中reduce函数计算.

3) 使用Function代替eval函数计算.

来测试这几种方式,那个性能比较好.

function generateRandomArray(len) {
    let arr = [];
    for (let i = 0; i < len; i++) {
        let value = Math.floor(Math.random() * len * 10);
        arr.push(value);
    }
    return arr;
}

function evalTest(randomArray, testLen) {
    let sum = 0;
    let sumArray = [];
    console.time('eval ' + testLen);
    for (var i = 0, len = randomArray.length; i < len; i++) {
        sumArray.push(randomArray[i]);
    }
    sum = eval(sumArray.join('+')); //使用eval计算表达式
    console.timeEnd('eval ' + testLen);
    console.error('eval:' + sum);
}

function forTest(randomArray, testLen) {
    let sum = 0;
    console.time('for ' + testLen);
    for (var i = 0, len = randomArray.length; i < len; i++) {
        //最简单直接的方式,直接求和运算
        sum += randomArray[i];    
    }
    console.timeEnd('for ' + testLen);
    console.error('for:' + sum);
}

function readuceTest(randomArray, testLen) {
    let sum = 0;
    console.time('reduce ' + testLen);
    sum = randomArray.reduce((previousValue, currentValue) => {
        return previousValue + currentValue
    }, 0);  //使用reduce
    console.timeEnd('reduce ' + testLen);
    console.error('reduce:' + sum);
}

function functionTest(randomArray, testLen) {
    let sum = 0;
    let sumArray = [];
    console.time('function ' + testLen);
    for (var i = 0, len = randomArray.length; i < len; i++) {
        sumArray.push(randomArray[i]);
    }
    //使用Function
    sum = new Function('"use strict"; return ' + sumArray.join('+') + ';')();
    console.timeEnd('function ' + testLen);
    console.error('function:' + sum);
}

function allTest() {
    var testLenArray = [16, 64, 128];
    for (var i = 0, len = testLenArray.length; i < len; i++) {
        let randomArray = generateRandomArray(testLenArray[i]);
        evalTest(randomArray, testLenArray[i]);
        forTest(randomArray, testLenArray[i]);
        readuceTest(randomArray, testLenArray[i]);
        functionTest(randomArray, testLenArray[i]);
    }
}
allTest();

先看看Edge浏览器执行结果:

使用Edge浏览器执行eval和Function性能测试

然后看看Firefox浏览器的执行结果:

使用Firefox浏览器执行eval和Function性能测试

通过测试结果得出:

1) 在循环内,直接求和的方式在数组长度不多的情况,性能最好

2) reduce在数组长度相对多之后,性能比for求值性能好

3) Function在性能上和eval半斤八两

个人能力有限,如果您发现有什么不对,请私信我

如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流

val加密的终极用法

Eval加密,做为一种传统且古老的JS代码加密方法,相信很多人都知道。

例如这个在线Eval加密:

https://www.fairysoftware.com/js_jia_mi_eval.html

但这种Eval加密,其实并不能算真正的加密,只能算一种编码,可以被很容易的逆向得到原始代码。

但本文要介绍的,并非这种Eval加密。

而是借助Eval,并结合专业JS混淆加密所实现的,用这个方法加密得到的JS代码,安全强度非常高。

下面,进行演示:

例如一段JS代码:

function get_copyright(){

var domain = "jshaman.com";

var from_year = 2017;

var copyright = "(c)" + from_year + "-" + (new Date).getFullYear() + "," + domain;

return copyright;

}

console.log(get_copyright());

用jshaman(https://www.jshaman.com/)进行混淆加密:

得到如下加密代码:

如上图所示,虽然加密后的代码已经非常混乱。

但如果希望console、函数名也“消失”,则可以借且eval。

将原始代码改造成以下形式:

var str = `

function get_copyright(){

var domain = "jshaman.com";

var from_year = 2017;

var copyright = "(c)" + from_year + "-" + (new Date).getFullYear() + "," + domain;

return copyright;

}

console.log(get_copyright());

`

eval(str)

说明:将原来的代码整体做为字符串都包裹在了eval中执行。

再对上面的代码进行混淆加密:

加密生成了新的代码:

在新生成的代码中,console、函数名,都找不到了,原始代码的线索彻底消失。

因为在用JShaman混淆加密时,把原始代码整体当做字符串加密了,原本代码中的语法关键字、系统变量、函数名等等,全被加密了。

通常来说,直接用JShaman加密得到的JS代码,已可满足99%的需求。但如果遇到特别的场合、加密强度要求的极高,那么,可以用本文讲述的方法,对JS代码改造再进行混淆加密。但需注意:转成字符串的代码量不可太大。

val()的使用

eval() 函数可将字符串转换为代码执行,并返回一个或多个值

eval调用时,实例为eval( "( javascript代码 )" )

eval()的返回值

eval()的返回值遵循以下规则:

1.如果eval()的参数不是字符串,那么eval()将直接返回参数。

2.如果eval()的参数是字符串,那么eval()将这个字符串解析成代码后进行执行,并返回最后一行代码执行的结果。

3.如果字符串无法解析成合法的代码,eval()将抛出SyntaxError错误。

举例1(eval的参数不是字符串):

运行结果(谷歌浏览器测试):

举例2(eval的参数是字符串):

运行结果(谷歌浏览器测试):

当然,如果不使用eval()方法,上面的代码可以使用匿名函数写

举例3(字符串无法解析成合法的代码):

运行结果(谷歌浏览器测试):此时可以看到 谷歌浏览器控制台报错

eval()的兼容性问题

IE6/7/8不兼容

使用IE8来测试代码:

没有弹出框,控制台报错:

解决方法:

a)var s = "[function(){alert('Test!')}][0]";

b)var s = "0?0:function(){alert('Test!')}";

当然这个解决方法是从国外论坛里面找到(网站:http://stackoverflow.com/questions/6807649/problems-with-ie9-javascript-eval)

大意是:这在JScript解释器里面是一个bug,它不会出现在IE9除非你使用混杂模式或兼容来看。IE8错误将这个函数表达式解释为函数的声明,使得它没有任何的返回值。所以你可以写成其他比较典型的表达式,从从而在JScript解释器中构成一个表达式

那么我们就知道IE6/7/8使用JScrip解释器来解析eval()把参数当初函数声明,没有返回值,所以我么可以把eval()函数里面的字符串代码写成一个表达式,即可以写成:

[function(){alert('Test!')}][0] 或 0?0:function(){alert('Test!')}

改代码:

在IE8里面测试结果:

最后利用ietester工具测试在IE6也同样没有问题。