翻译:用Javascript的Function构造器伪造上下文 by Ben Nadel

 

在我的jQuery模板标记语言(JTML)项目中,我需要一种方式将JTML模板编译到JS函数,这样它们就可以如期地在任何时候转换成新的HTML标记。但这是一个严峻的问题,因为JTML代码涉及非作用域(全局)变量,在函数执行的时候,那些变量能够在渲染引擎的上下文中可用。当我编写这个项目的代码时,我能想到的唯一办法就是当一个模板需要被渲染时每次创建一个新的Function()。这样,在一定程度上违背了提前编译渲染引擎的目的。经过深思熟虑,我想是否可以通过JS的apply()功能创建出动态的作用域,预编译函数。

 

当你使用apply()或者call()方法去改变一个函数的执行上下文时,其实真正做的就是改变“this”的引用。this在非作用域下并没有效果,但会向函数的闭包链往上爬找到this(全局对象)。但是,如果我们有这样一个函数可以在它执行完毕前检查它自己的this上下文?如果我们创建了一个函数,在该函数执行完毕之前在前面插入有“this作用域“变量到它的局部作用域,我们就应该能够通过改变函数的上下文,简单地动态改变可用的非作用域变量了。下面的demo就是我要说的:

<!DOCTYPE HTML>
<html>
<head>
    <title>Javascript Function() With Context</title>
    <script type="text/javascript" src="jquery-1.4.2.js"></script>
    <script type="text/javascript">

        // I am a proxy for the Function() constructor that prepends
        // code to copy the function's context variables into the
        // function local scope so that they may dynamically changed
        // at the time of execution.
        // Function构造器代理。 在函数代码块头部插入复制函数的上下文变量到函数局部作用域。
        function FunctionContext( sourceCode ){

// Call and return the Function() constructor. Notice
// that if the THIS is not Window, we are VAR'ing the
// context variables into the function local scope.
            return(Function(
                    "if (this != window){" +
                            "for (var __i in this ){" +
                            "eval( 'var ' + __i + ' = this[ __i ];' );" +
                            "}" +
                            "}" +
                            sourceCode
            ));
        }


        // -------------------------------------------------- //
        // -------------------------------------------------- //


        // Define a function that uses a variable that is not
        // defined as part of the function source code.
        var saySomething = FunctionContext(
                "console.log( name + ', you\\\'re looking so hot!' );"
        );


        // -------------------------------------------------- //
        // -------------------------------------------------- //


        // Now, execute the saySomething() method in three different
        // contexts, each of which changes the name.
        saySomething.apply({
            name: "Tricia"
        });

        saySomething.apply({
            name: "Vicky"
        });

        saySomething.apply({
            name: "Erika"
        });

    </script>
</head>
<body>
<!--- Intentionally left blank. --->
</body>
</html>

我已经创造了这个函数FunctionContext(),实质上就是JS的Function()构造器代理。它的工作就是在代码块被传入Function构造器参数中前在头部插入一段逻辑。该逻辑检查函数的”this“上下文。如果函数的上下文不是window对象,说明它被call()或者apply()方法重写了,所有的this作用域值都被编译成函数局部作用域的变量了。任何上下文的变量在函数内部逻辑里都可以当做非作用域值了。

 

当我们跑上面的代码时,输出:

Tricia, you're looking so hot!
Vicky, you're looking so hot!
Erika, you're looking so hot!

 

 

正如所看,通过使用apply(),每次执行编译后的函数时,我都在改变上下文。而且,自从第一次执行复制”this“作用域变量到局部作用域,无作用于变量”name“从来不会报错。

 

我需要一种方式当一个函数被执行时动态改变变量为可用的。如果我单单用eval()函数,我将会失去预编译的优势。如果我仅用Function()构造器,我不能想到一个办法改变为可用变量,在没有使用已有的参数情况下(我可能甚至不知道该如何提前)。通过两个结合使用,我找到了最优雅的方式转换为可用的局部变量。

 

原文:http://www.bennadel.com/blog/1927-Faking-Context-In-Javascript-s-Function-Constructor.htm

posted @ 2013-12-11 17:11  LukeLin  阅读(418)  评论(0编辑  收藏  举报