JavaScript中的#ifndef

15

我正在寻找一种在Javascript中只定义函数一次的解决方案,类似于编译语言中的 #ifndef。我找到了几个库,据说可以模仿这种功能,但它们都没有起作用。

我正在使用MVC 3 Razor,并定义了一些HTML辅助程序,将基本上是用户控件的内容放到页面上。

每个控件都有一组JavaScript函数,为该控件定义特定的功能,因此问题在于:当多次调用辅助程序时,函数会被定义多次。

我希望能够找到一种方法,使非常少量的JavaScript代码仍然可以在辅助程序中定义,而不必将所有这些小辅助程序的JavaScript代码分割成单独的文件。

示例:

@helper CmsImage(int id)
{
  var Html = ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;

  <text>
    <input type="button" class="editor_function" style="display: none;" onclick="editImage(@id); return false;" />
    <script>
        function editImage(id) {
            $('#alt_text' + id).attr('value', $('#' + id).attr('alt'));
            $('#image_url' + id).attr('value', $('#' + id).attr('src'));
        }

        function saveImage(button, id) {
            $(button).parent().parent().removeClass('color-yellow').addClass('color-red');
            $(button).parent().siblings('div.widget-content').html('<img alt="' + $('#alt_text' + id).val() + '" src="' + $('#image_url' + id).val() + '" id="' + id + '" />');
        }
        #endif 
    </script>
    Image Url:
    <input type="text" id="image_url@{id.ToString();}" /><br />
    Alt Text:
    <input type="text" id="alt_text@{id.ToString();}" /><br />
    <input type="button" value="save" onclick="saveImage(this, @{id.ToString();});" />
    @Html.Raw(GetCurrentContent(id))
  </text>
}

在浏览器中,上述代码无法工作,它会给我一个错误:'48: Unrecognized token ILLEGAL'


不,浏览器不会这样做。但是如果您在计算机上有JavaScript文件,您可以通过C预处理器运行它们,并将结果提供给浏览器。 - icktoofay
你能解释一下为什么你需要这个功能,以及你打算如何使用它吗?这样更容易建议一个满足你需求的解决方案。 - Stuart Cook
我不明白那会如何解决我的问题。我会在我的问题中添加更多描述,以更好地说明我正在做什么。 - KenEucker
我想你可以使用一段JavaScript代码通过AJAX加载其他脚本,对它们进行预处理,然后再进行评估... - icktoofay
定义同一个函数多次会有问题吗? - Gabe
显示剩余3条评论
8个回答

14

我想你知道,Javascript没有像C/C++那样的预处理指令,但是你可以使用类似以下的在运行时评估的普通if语句:

if (typeof myFunc === "undefined") {
    var myFunc = function(a,b) {
        // body of your function here
    }
}

或者对于整个函数库:

if (!window.controlUtilsDefined) {
    window.controlUtilsDefined = true;

    // put control library functions here

    function aaa() {
        // body here
    }

    function bbb() {
        // body here
    }

}

或者,如果您想基于其他变量进行检查:

var myFunc;
if (debugMode) {
    myFunc = function(a,b) {
        // body of your function here
    }
} else {
    myFunc = function(a,b) {
        // body of your alternate function here
    }
}

如果你担心的只是每个控件在库中都有多个完全相同的函数名称,那在Javascript中这不是一个实际问题。定义的最后一个将是有效的,但如果它们都是相同的,那就不是实际问题。只有一个定义将存在于内存中,因为后面的定义将替换前面的定义。

如果你控制控件的源代码,那么最好将公共工具分别放在它们自己的JS文件中,并且使主机页面只引用一次该公共工具脚本文件。

或者(需要更多的工作但对主机页面没有额外的责任),每个控件可以动态地从外部JS文件加载其工具,并检查已知的全局变量,以查看其他控件是否已经加载了公共外部JS。


12

如果你有一些构建脚本,我建议使用GPP预处理器(http://en.nothingisreal.com/wiki/GPP, 或者win版本 http://makc.googlecode.com/svn/trunk/gpp.2.24-windows/)

所以你需要执行以下步骤:

  1. gpp -o _script.js script.js (其中_script.js是具有预处理器命令的源文件)
  2. (可选) 压缩 script.js (使用google closure编译器等工具)
  3. 将 script.js 部署到您的 Web 文件夹中。

这样可以得到最优化的 js 代码,并且您不需要运行时检查

#define debugMode
#ifdef debugMode
    var myFunc = function(a,b) {
        // body of your function here
    }
#else
    var myFunc = function(a,b) {
        // body of your alternate function here
    }
#endif

我应该给gpp什么参数来设置debugMode? - feedc0de
Diniel,请尝试执行以下命令: gpp -DdebugMode=1 -o _script.js script.js 并从代码中删除 #define debugMode。 - Max

5

我看到jfriend提供的答案有点过时,当时还没有node.js。请检查最新的preprocessor.js(可以通过npm install获取)。

您可以使用以下静态条件(来自文档):

 // #ifdef FULL
console.log("Including extension");
// #include "path/to/extension.js"
// #else
console.log("Not including extension");
// #endif

使用方法如下:


    Usage: preprocess sourceFile [baseDirectory] [-myKey[=myValue], ...] [> outFile]

    preprocess Source.js . -FULL=true > Source.full.js


1
弃用通知:Preprocessor.js已被MetaScript取代,后者是一种更加JavaScript化的构建时元编程方式,使用JavaScript本身作为元语言。查看迁移指南以快速了解其优点。 - Dima Fomin
Metascript“已被所有者存档。现在它是只读的。” :/ - dfrankow

4

虽然这是一个老话题,但对于任何对解决方案感兴趣的人,我编写了jspreproc来部署一些riot模块,所有这些都是适用于node 0.10.0及以上版本的JavaScript。

jspreproc是开源的,删除空行,支持保留不同类型注释的过滤器,以及C语言风格中的条件注释:

  • #if、#elif、#else、#endif 使用表达式和 defined() 支持
  • #define 使用表达式和基本宏替换
  • #ifdef、#ifndef
  • #include、#include_once

安装和使用示例:

$ npm -g i jspreproc
$ jspp -D RELEASE --empty-lines 0 lib/file1.js lib/file2.js > dist/app.js

请访问github上的jspreproc代码库了解更多相关的IT技术信息。


2
这是一个旧问题,但现有的答案已经过时。如果你想要更好的内联或消除死代码,你可以使用谷歌的闭包编译器/** @const */助手。
以下是@ const助手与高级优化一起应用的完全随机示例(仅用于演示,毫无意义):
/** @const */
var G=true

if(G){var d=document}
function dummy(){if(!G){var d=document}return d.getElementsByTagName("body")[0]}
function hello(name) {
alert(dummy() + name + dummy());
}
hello('New user');

编译为:alert((void 0).getElementsByTagName("body")[0]+"新用户"+(void 0).getElementsByTagName("body")[0]);

或者不使用@const:var a=!0;function b(){if(!a)var c=document;return c.getElementsByTagName("body")[0]}alert(b()+"新用户"+b());

这样做的好处是,代码在开发过程中不需要每次运行前进行预处理即可正常工作。(还有更好的优化效果)


1
在现代,这看起来是RequireJS或等效的AMD模块的工作。 所有其他答案都谈论预编译和缩小 - 这是好的,并且应该另外考虑。

0

JavaScript中不可能使用预处理器或#define函数... JavaScript是一种松散耦合和解释性语言..... 但您可以在TypeScript中使用它,因为它是强耦合且使用编译器的语言......


-1
如果使用像Apache(和XAMPP用于本地开发)这样的Web服务器,请使用PHP作为您的预处理器。只需将扩展名从.js更改为.php,并插入任意数量(包括零)的PHP标记即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接