• JavaScript忍者秘籍——运行时代码求值


    1. 代码求值机制

      JavaScript中,有很多不同的代码求值机制。

      ● eval()函数

      ● 函数构造器

      ● 定时器

      ● <script>元素

    - 用eval()方法进行求值

      作为定义在全局作用域内的eval()方法,该方法将在当前上下文内,执行所传入字符串形式的代码。

      基本功能

      ● 该方法将执行传入代码的字符串

      ● 在调用eval()方法的作用域内进行代码求值

     例如:

    eval("5+5")     // 10
    
    (function(){
        eval("var ninja = 6");
        console.assert(ninja === 6, "evaluated within the current scope.");      
    })();

      求值结果

      eval()方法将返回传入字符串中最后一个表达式的执行结果。例如,如果我们调用如下语句:

      eval("3+4; 5+6");    //  结果将返回11

      应该指出的是,任何不适简单变量、原始值、赋值语句的内容都需要在外面包装一个括号以便返回正确的结果。例如,如果我们想使用eval()创建一个简单的对象,可能会编写如下语句:

      var o = eval('({ninja: 1})');

      但是,结果不是我们所期望的。我们需要在对象字面量外面包装一个括号,示例如下:

      var o = eval('({ninja: 1})');

      在IE8或之前的版本,要使用布尔表达式,才能让eval()进行正确的调用,如下代码:

    var fn = eval("false||function(){return true;}");

      就像我们用普通方式在特定作用域内创建函数一样,eval()创建的函数会继承该作用域的闭包——局部作用域内执行eval()时的衍生结果。

    - 用函数构造器进行求值

      使用Function构造器来实例化函数,示例如下:

    var add = new Function("a", "b", "return a+b;");

      Function构造器可变参数列表的最后一个参数,始终是要创建函数的函数体内容。前面的参数则表示函数的形参名称。所以,上述示例代码等价于如下代码:

    var add = function(a,b){return a + b}

      虽然这些代码在功能上是等同的,但采用Function构造器方式有一个明显的区别,函数体尤运行时的字符串所提供。另外一个极其重要的实现区别是,使用Function构造器创建函数的时候,不会创建闭包。在不想承担任何不相关闭包开销时,这可能是一件好事。

    - 用定时器进行求值

      还有一种方式,可以让代码字符串进行求值,而且是异步的,那就是通过定时器进行求值。示例如下:

    var tick = window.setTimeout("alert("Hi!"), 100")

    - 全局作用域内的求值操作

    function globalEval(data){
        data = data.replace(/^s|s*$/g, "");
        if(data){
            var head = document.getElementsByTagName("head")[0]||document.documentElement, script = document.createElement("script");
            script.type = "text/javascript";
            script.text = data;
            head.appendChild(script);
            head.removeChild(script);
        }
    }
    
    window.onload = function(){
        (function(){
            globalEval("var test = 5;");
        })();
    }

      在eval()方法中,我们定义了一个名为globalEval()的函数,以便在全局作用域内求值任何想要要求的内容。该函数去除了所传字符串中的所有前导和尾部空白字符,然后定位DOM中的<head>元素或文档本身,并创建一个分离的<script>元素。设置script元素的类型,然后把需要求值的字符串加载到该script元素的body内。将script元素附加到DOM上,作为head元素的一个子节点,将会导致该脚本在全局作用域内进行求值。

      这段代码最常见的场景是动态执行从服务器端返回的代码。它几乎总是要求代码在全局作用域内进行执行,从而让新函数的使用变成必然。

    2.函数反编译

        反编译意味着将程序集或字节码重组成源代码。例如,将函数反编译成字符串:

    function test(a){return a+a; }
    console.assert(test.toString() === "function test(a){return a+a; }","Function decompiled");

    3.代码求值实战

    - JSON转化

      运行时求值的最广泛使用方式是将JSON字符串转换为JavaScript对象表示法。例:

    var json = '{"name":"Ninja"}';
    var object = eval("("+ json +")");
    assert(object.name === "Ninja", "My name is Ninja!");

    - 导入有命名空间的代码

      对于将命名空间导入到当前上下文,base2库提供了一个非常有趣的解决方案。因为没有办法将该问题进行自动化操作,因此我们可以利用运行时求值让该实现变得更简单。

      每当一个新类或模块添加到base2包的时候,构造可执行代码的字符串,对其进行求值,可以将产生的函数引入到当前上下文中,示例如下:

    base2.namespace == 
        "var Base = base2.Base; var Package = base2.Package;" +
        "var Abstract = base2.Abstract; var Module = base2.Module;" +
        "var Enumberable = base2.Enumberable; var Map = base2.Map;" +
        "var Collection = base2.Collection; var RegGrp = base2.RegGrp;" +
        "var Undefined = base2.Undefined; var Null = base2.Null;" +
        "var This = base2.This; var True = base2.True; var False = base2.False;" +
        "var assignID = base2.assignID; var detect = base2.detect;" +
        "var global = base2.global; var lang = base2.lang;"+
        "var JavaScript base2.JavaScript; var JST = base2.JST;" +
        "var JSON = base2.JSON; var IO = base2.IO; var MiniWeb = base2.MiniWeb;" +
        "var DOM = base2.DOM; var JSB = base2.JSB; var code = base2.code;" +
        "var doc = base2.doc;"
  • 相关阅读:
    位运算 & 网络序字节序
    TFTP & commons-net-3.3.jar
    存储过程
    poj1185-炮兵阵地(状态压缩dp)
    hdu4570-区间dp
    codevs1026-dp(记忆化搜索)
    hdu1494 跑跑卡丁车(动态规划)
    hdu5094-Maze
    hdu4403- A very hard Aoshu problem(搜索)
    hdu2510-符号三角形(dfs+打表)
  • 原文地址:https://www.cnblogs.com/koto/p/5826306.html
Copyright © 2020-2023  润新知