zoukankan      html  css  js  c++  java
  • 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;"
  • 相关阅读:
    el-select下拉框选项太多导致卡顿,使用下拉框分页来解决
    vue+elementui前端添加数字千位分割
    Failed to check/redeclare auto-delete queue(s)
    周末啦,做几道面试题放松放松吧!
    idea快捷键
    解决flink运行过程中报错Could not allocate enough slots within timeout of 300000 ms to run the job. Please make sure that the cluster has enough resources.
    用.net平台实现websocket server
    MQTT实战3
    Oracle 查看当前用户下库里所有的表、存储过程、触发器、视图
    idea从svn拉取项目不识别svn
  • 原文地址:https://www.cnblogs.com/koto/p/5826306.html
Copyright © 2011-2022 走看看