JavaScript中的嵌套文字,一个好主意?

时间:2011-01-15 17:21:11

标签: javascript design-patterns

我偶然发现了JavaScript中的一种模式,我将其称为“嵌套文字”。它的工作原理是因为你可以将自调用函数文字作为自调用函数文字的参数。

自调用函数文字是一个立即评估自身的函数,如下所示:

(function(arg1, arg2) { return arg1 + arg2; }) (1, 2);

这相当于写作

3;

使用这种自调用函数文字的一个优点是它的代码是严格自上而下的,这使它很容易理解。考虑

document.write(
    (function(
        factorial,
        square
    ) {
        return factorial(square(3));
    } (
        function f(x) {
            if (x <= 1) return 1;
            else return x * f(x - 1);
        },
        function f(x) {
            return x*x;
        }
    ))
);

假设你将document.write()的参数识别为自调用函数文字,你不必非常聪明地理解这个程序将三的平方的阶乘写入页面,并理解如何实现阶乘和平方函数。该程序直观地读作“在文档中,写出3的平方的阶乘,其中阶乘是...而正方形是......”。

更重要的是,没有什么可以阻止你创建一个自调用函数文字的参数,就像函数文本本身一样。例如:

(function(arg1, arg2) { return arg1 + arg2 }) (
    1,
    (function(arg1, arg2) { return arg1 + arg2 })(2, 3)
);

这相当于写作

6;

这种嵌套可以继续进行,允许你制作非常复杂的文字,这些文字很容易理解。考虑最终的计划:

document.write(
    /* text = */ (
        (function(
            title,
            section1,
            section2,
            section3
        ) {
            return ('<h3>' + title + '</h3>' +
                    section1 +
                    section2 +
                    section3
                   );
        } (
            /* title = */ (
                'Nested Literals in JavaScript'
            ),
            /* section1 = */ (
                (function(
                    title_section1,
                    paragraph1_section1,
                    paragraph2_section1
                ) {
                    return ('<h4>' + title_section1 + '</h4>' +
                            '<p>' + paragraph1_section1 + '</p>' +
                            '<p>' + paragraph2_section1 + '</p>'
                           );
                } (
                    /* title_section1 = */ (
                        'Nested Literals'
                    ),
                    /* paragraph1_section1 = */ (
                        "JavaScript allows nested literals through its self invocating function literals.  This is because self invocating function literals take literals as arguments.  Therefore, a self invocating function literal can take an argument that is a self invocating function literal that can take an argument that is ... ."
                    ),
                    /* paragraph2_section1 = */ (
                        "This is cool! for it allows arbitrarily complex literals.  This program consists out of a single call to document.write(), its argument being relatively complex."
                    )
                ))
            ),
            /* section2 = */ (
                (function(
                    title_section2,
                    paragraph1_section2,
                    paragraph2_section2,
                    paragraph3_section2
                ) {
                    return ('<h4>' + title_section2 + '</h4>' +
                            '<p>' + paragraph1_section2 + '</p>' +
                            '<p>' + paragraph2_section2 + '</p>' +
                            '<p>' + paragraph3_section2 + '</p>'
                           );
                } (
                    /* title_section2 = */ (
                        'Top-Down Coding'
                    ),
                    /* paragraph1_section2 = */ (
                        "Using nested literals, your code can become very readable.  Reading the code from top to bottom you will get to understand the structure top-down, which is how we (at least I) like it."
                    ),
                    /* paragraph2_section2 = */ (
                        "This code is an example of this.  What it does is probably obvious on your first read.  If not, then it is probably because you are not familiar enough with self invocating function literals, yet."
                    ),
                    /* paragraph3_section2 = */ (
                        "And not only does it makes programs easy to read, it makes them easy to write."
                    )
                ))
            ),
            /* section3 = */ (
                (function(
                    title_section3,
                    paragraph1_section3,
                    paragraph2_section3,
                    paragraph3_section3
                ) {
                    return ('<h4>' + title_section3 + '</h4>' +
                            '<p>' + paragraph1_section3 + '</p>' +
                            '<p>' + paragraph2_section3 + '</p>' +
                            '<p>' + paragraph3_section3 + '</p>'
                           );
                } (
                    /* title_section3 = */ (
                        'Local Variables Only'
                    ),
                    /* paragraph1_section3 = */ (
                        "Because everything is literal, or anonymous, no assignments are made.  This keeps the global scope completely clean."
                    ),
                    /* paragraph2_section3 = */ (
                        "Also, arguments to a self invocating function literal are outside the scope of the body of the self invocating function literal.  This makes their scopes completely separated.  Even though their behaviour is nested, their scopes are not." 
                    ),
                    /* paragraph3_section3 = */ (
                        "This puts nested literals in contrast to nested functions, which allow their behaviour to get tangled up with the variables of the function in which they are nested."
                    )
                ))
            )
        ))
    )
);

我认为这很明显。您可以通过点击地址栏的文字字段,输入“javascript:”,粘贴上述源代码并按Enter键来运行它。

我不确定的是嵌套文字的使用效率如何。我想知道你是否会遇到任何麻烦,如果你想把它带到极端。我想知道是否有人知道。

无论如何,我只是想分享一下,也许听听你的想法。

问候,Pieter Jan

2 个答案:

答案 0 :(得分:3)

我要说的是:确保您的代码可读。不要做这样的事情只是因为你可以这样做很酷。如果它使您的代码更具可读性,那就去做吧。有时它会这样做,有时肯定不会。

答案 1 :(得分:1)

我至少可以看到两个主要原因,这不是一个好主意

  • 你的代码变得越来越慢 - 每次你在javascript中进行一个函数调用时,一个新的callstack必须在其他东西之间进行初始化,如果函数做的很简单并且只需要发生一次,这是一个巨大的开销这个例子就是为什么做一个正常的循环要比使用jQuerys .each等方便的方法快得多,它会在每次迭代时调用一个函数。

  • 如果你需要做两次或更多次同样的事情,请使用函数,但给它们命名并引用它们,这样你的代码将更容易维护,命名函数也会给你带来好处可分析 - 尝试分析许多散布着匿名函数的javascript就像用一只手拍手一样。