zoukankan      html  css  js  c++  java
  • JavaScript中function的动态执行

     

    由于最近来自重构中的需要,所以深入的研究了JavaScript中 function(函数/方法)的动态执行。搜索了一下,发现在网上询问相关问题的人非常多,相应给出的解决方法也是很多的,但却没有深入研究的说明。本 人觉得深入的研究并解决function的动态执行问题还是非常有价值的。

    本文将从不同的应用情况入手,并由浅入深的给出解决方案与分析。动态执行从服务端返回的JavaScript代码不在本文的讨论范围内。

    场景1:动态执行无参数、无返回值function

    这是最简单,也是最常见的case。这种场景下,使用eval或者setTimeout都是可以的。如下示例代码:

    1 functiontest() {
    2     alert('test');
    3 }
    4  
    5 eval("test()");
    6 setTimeout("test()", 0);

    由于这种case是最简单的,如何去执行参考示例即可。想多做一点说明的是setTimeout这个方法。

    对于目前的JavaScript引擎来说,都是单线程处理任务的(JavaScript engines only have a single thread)。John Resig在他的How JavaScript Timer Work中 有详细的说明,这里不做累述。对于JavaScript的Timer来说,setTimeout与setInterval都是可以异步的执行代码的,但是 它们却在执行时有非常本质的区别(区别仍可参考How JavaScript Timer Work),被推荐使用的是setTimeout方法。既然setTimeout可以用来异步执行JavaScript的代码(function),那么 我们一旦将它的第二个参数设置为0,即0毫秒后执行第一个参数内容的话,就相当于立即异步执行代码。这样,通过使用多个setTimeout方法,便以另 一种形式在JavaScript中实现多线程。

    场景2:动态执行简单类型参数、无返回值的function

    这个case虽然要比场景1复杂一些,但是仍然非常简单即可完成。

    1 functiontest(input) {
    2     alert(input);
    3 }
    4  
    5 vart1 = 1;
    6 eval("test("+ t1 + ")");
    7 vart2 = true;
    8 setTimeout("test("+ t2 + ")", 0);

    场景3:动态执行复杂类型参数、无返回值的function 

    这个也不复杂,就怕想复杂了。如果按照场景2中的方式来动态执行,那么多半你会比较郁闷,因为想传递复杂类型的参数比较困难。简单的方式如下:

    1 functiontest(input) {
    2     alert(input[0].name);
    3 }
    4  
    5 vart = [{'name': 'db'}];
    6 eval("test(t)");
    7 setTimeout("test(t)", 0);

    可以看出,只需要将变量直接写到function字符串的参数部分即可。这种书写方法比较依赖于既定义的变量,我们改变一下代码再做一个测试,代码如下:

    01 functiontest(input) {
    02     alert(input[0].name);
    03 }
    04  
    05 functionprint() {
    06     vart = [{'name': 'db'}];
    07     eval("test(t)");
    08     setTimeout("test(t)", 0);
    09 }
    10  
    11 print();

    在不同的浏览器上,可以看到在eval输出了一次之后,setTimeout要么是没有输出,要么就是提示说“t未定义”。这是因为 setTimeout第一个参数,是存在作用域问题。只有在全局作用域定义的函数和变量,才能被setTimeout使用。这一点一定要注意,如果非要使 用非全局的函数与变量,只有考虑闭包来实现。

    场景4:动态执行有返回值的function
    在前边一些case中,已经解决了动态执行的function的传参问题,对于大多数的开发者来说就已经足够了。但是,在使用JavaScript进行基于配置的功能开发时,可能还会遇到需要动态执行有返回值function的case。

    之前几个case中,我们实现动态执行时,使用的是eval与setTimeout。可以提前否定的是setTimeout,因为这个方法已经固定 了返回值,即当前计时器的引用,用来清理计时器时使用。当然,提前否定setTimeout让很多人不甘心,并给出了如下方法得到返回值:

    1 functiontest(input) {
    2     return"result:"+ input;
    3 }
    4 vart = 'test';
    5 setTimeout("alert(test(t))", 0);

    执行后发现,返回值的确被打印了出来。但是,如果我想使用这个返回值呢?我们将代码变化一下:

    1 functiontest(input) {
    2     return"result:"+ input;
    3 }
    4 vart = 'test';
    5 varresult = '';
    6 setTimeout("result = test(t)", 0);
    7 alert(result);

    如果得到最后一行的输出结果,所有人都会失望。前文已经提过,setTimeout是异步执行的,所以在第一个参数中的JavaScript代码执行时,result就已经输出了。显然,setTimeout要处理这个问题比较苦难。

    eval在这个case中比较給力,能够完成我们交给的任务:

    01 functiontest(input) {
    02     return"result:"+ input;
    03 }
    04 varresult;
    05 vart = 'tttt';
    06 result = eval("test(t)");
    07 alert(result);
    08  
    09 t = '2222';
    10 eval("result = test(t)");
    11 alert(result);

    两种写法均可以得到我们希望的结果。而且复杂一些的需要也能胜任:

    01 Test = function() {
    02 };
    03 Test.prototype = {
    04     test : function(input) {
    05         return"result:"+ input;
    06     }
    07 };
    08  
    09 varte = newTest();
    10 varresult;
    11 vart = 'tttt';
    12 result = eval("te.test(t)");
    13 alert(result);

    还有一种方式,也可以满足这个case:

    01 Test = function() {
    02 };
    03 Test.prototype = {
    04     test : function(input) {
    05         return"result:"+ input;
    06     }
    07 };
    08  
    09 vart = 'tttt';
    10 varte = newTest();
    11 varvalue = newFunction("return te.test(t)")();
    12  
    13 alert(value);

    但是,new Function的方式却存在作用域的问题,而且也需要借助闭包才能解决。问题如下:

    01 Test = function() {
    02 };
    03 Test.prototype = {
    04     test : function(input) {
    05         return"result:"+ input;
    06     }
    07 };
    08  
    09 functionrun() {
    10     vart = 'tttt';
    11     varte = newTest();
    12     varvalue = newFunction("return te.test(t)")();
    13  
    14     alert(value);
    15 }
    16  
    17 run();

    而eval就不存在作用域的问题:

    01 Test = function() {
    02 };
    03 Test.prototype = {
    04  test : function(input) {
    05   return"result:"+ input;
    06  }
    07 };
    08  
    09 functionrun() {
    10  varte = newTest();
    11  varresult;
    12  vart = 'tttt';
    13  result = eval("te.test(t)");
    14  alert(result);
    15 }
    16  
    17 run();

    总结:
    在处理动态执行的问题上,推荐使用eval。它是同步处理的,而且无作用域问题,复杂参数、返回值均可满足。

  • 相关阅读:
    hibernate事务管理
    oracle的中文排序问题
    hibernate一级缓存
    Hibernate的实体规则、主键生成策略、对象状态
    【SVN】命令行忽略不必要的文件和文件夹
    【SVN】SVN的trunk、branches、tag的使用以及分支的概念
    hibernate介绍及环境搭建
    敏捷实践:比每日会议更疯狂的半日会议!
    Android开发之有效获取状态栏(StatusBar)高度
    jquery判断输入文字个数的统计代码
  • 原文地址:https://www.cnblogs.com/firstdream/p/2350444.html
Copyright © 2011-2022 走看看