为什么需要CDATA而不是以同样的方式工作?

时间:2012-06-22 17:56:41

标签: javascript xml cross-browser greasemonkey e4x

在Firefox和Chrome的控制台中,这有效(警报脚本内容):

var script = document.createElement("script");
script.textContent = (
    function test() {
        var a = 1;
    }
);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

将此代码用作Firefox的Greasemonkey脚本也可以。

现在,如果要将“私有方法”do()添加到test()它不再适用,无论是Firefox / Chrome控制台还是Greasemonkey脚本:

var script = document.createElement("script");
script.textContent = (
    function test() {
        var a = 1;
        var do = function () {
            var b = 2;
        };
    }
);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

要在Greasemonkey脚本中完成此工作,我必须将所有代码放在CDATA 标记块中:

var script = document.createElement("script");
script.textContent = (<![CDATA[
    function test() {
        var a = 1;
        var do = function() {
            var b = 2;
        };
    }
]]>);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

这仅适用于Greasemonkey脚本;它会从Firefox / Chrome控制台引发错误。我不明白为什么我应该使用CDATA标签,我没有XML规则要尊重,因为我没有使用XHTML。

要使其在Firefox控制台(或Firebug)中运行,我需要将CDATA放入<></>等标记中:

var script = document.createElement("script");
script.textContent = (<><![CDATA[
    function test() {
        var a = 1;
        var do = function() {
            var b = 2;
        };
    }
]]></>);
document.getElementsByTagName("head")[0].appendChild(script);
alert(document.getElementsByTagName("head")[0].lastChild.textContent);

这不适用于Chrome控制台。我尝试在最后添加.toString(),就像许多人一样(]]></>).toString();),但它没用。

我尝试用标记名<> </>替换<foo></foo>,但这也不起作用。

如果我在另一个函数中定义var do = function(){},为什么我的第一个代码段不起作用?

即使我没有使用XHTML,为什么还要使用CDATA作为解决方法?

如果没有使用Greasemonkey脚本,我为什么要为Firefox控制台添加<> </>

最后,Chrome和其他浏览器的解决方案是什么?

修改

我的不好,我从未在JS中使用do-while而且我在一个简单的文本编辑器中创建了这个例子,所以我没有看到“do”是一个保留的关键字:p

但问题仍然存在,我没有在我的示例中初始化Javascript类。 使用这个新示例,Greasemonkey需要CDATA,而E4X CDATA <>之间的Firefox需要</>,Chrome就会失败:

var script = document.createElement("script");
script.textContent = (
<><![CDATA[var aClass = new AClass();
function AClass() {
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();]]></>
);
document.getElementsByTagName("head")[0].appendChild(script);

问题:为什么?

2 个答案:

答案 0 :(得分:4)

这里有多个错误/问题:

  1. 添加私有方法do()无效,因为do is a reserved word
    此代码在FF和Chrome(以及GM脚本)中均可正常运行:

    var functionVar = (
        function test() {
            var a = 1;
            var properlyNamedVariable = function() {
                var b = 2;
            };
        }
    );
    console.log (functionVar.toString() );
    

    如果您使用描述性变量名称,您将来不仅会为自己节省多少悲伤,而且几乎不会意外地使用保留字。

  2. 回复:"To make this work in a Greasemonkey script, I have to put all code in a CDATA tag"

    Nope,该代码工作。发生的事情是在Greasemonkey的沙箱中eval'范围内发生了错误,但未报告Firefox的错误控制台。也就是说,它默默地失败了 如果您尝试从控制台执行test();,则会收到错误:ReferenceError: test is not defined。然而,如果您删除var do = function ...代码并重新加载页面,则可以从控制台调用test()

  3. 同样,script.textContent = (<><![CDATA[ ...代码也不起作用。它也默默地失败了,test()没有被定义。与前一个示例相比,这种情况略有不同。

  4. 依赖于(<><![CDATA[ ...技巧的代码在Chrome中不起作用,因为Chrome不支持在javascript中内联处理XML。这是Firefox which supports E4X的特权,而Chrome不是。

  5. format your code,这样做不是一件苦差事。


  6. 所以,在摘要中:

    1. 为变量,函数等使用描述性名称
    2. 请注意您使用的语言中的保留字。
    3. 小心无声的失败。不幸的是,这些都发生在各种Greasemonkey场景中。
    4. 不同的浏览器支持不同的功能。
    5. 这是一种适用于Firefox,Chrome,其他几种浏览器以及Greasemonkey,Tampermonkey,Scriptish等的技术。它还有助于调试和测试,因为代码与内联变量声明分开:

      1. 正常定义您的函数或代码:

        function testMyAdhocCode () {
            "use strict";    
            var importInteger   = 1;
            var imA_PrivateFunction = function () {
                var someOtherInteger = 2;
        
                console.log (
                    "importInteger = ", importInteger,
                    "  ||  someOtherInteger = ", someOtherInteger
                );
            };
        
            console.log ("Greetings from testMyAdhocCode()!");
            imA_PrivateFunction ();
        }
        


      2. 然后将其注入页面(如果必须的话。最好不要注入代码,如果你能提供帮助的话。):

        addJS_Node (testMyAdhocCode);   //-- Function is just created.
        
        // OR
        addJS_Node (null, null, testMyAdhocCode); //-- Function is created and immediately run.
        
        // OR
        addJS_Node ("var someVar = 86;") //-- Adhoc code
        
        // OR
        addJS_Node ("preExistingFunction (42); ") //-- Run a preexisting function.
        


      3. 您的脚本中包含addJS_Node()的位置为:

        function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
            var D                                   = document;
            var scriptNode                          = D.createElement ('script');
            if (runOnLoad) {
                //--- Doesn't always fire on Chrome. Needs @run-at document-start.
                scriptNode.addEventListener ("load", runOnLoad, false);
            }
            scriptNode.type                         = "text/javascript";
            if (text)       scriptNode.textContent  = text;
            if (s_URL)      scriptNode.src          = s_URL;
            if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';
        
            var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
            targ.appendChild (scriptNode);
        }
        



      4. 更新其他问题:

        新问题更加相似。

        • 这是不正确的代码,所以它在Firefox控制台中引发错误。
        • 它使用E4X功能,因此无法在Chrome中使用。
        • 应该在Greasemonkey中抛出与在Firefox中相同的错误(它们名义上是相同的JS引擎),但是由于关于GM沙盒脚本的纯粹愚蠢运气,你可以逃脱 错误代码 (暂时)。
        • 不要这样编码!使用addJS_Node()等函数。

        如果你真的必须注入代码,那么正确的方法就是这样:

        function main () {
            "use strict";   // Keep this line!
            var aClass = new aClass();
        
            function aClass() {
                var a = 1;
                var aPrivateMethod = function() {
                    var b = 2;
                    alert(b);
                };
                this.aPublicMethod = function() {
                    var c = 3;
                    alert(c);
                };
            }
        
            aClass.aPublicMethod();
        
            //-- PUT ALL OF THE REST OF YOUR INJECTED CODE HERE.
        }
        
        addJS_Node (null, null, main);
        

        将所有内容放在目标页面的 global 范围内是一个坏主意!但是,如果你坚持这样做,代码是这样的:

        function aClass() {
            "use strict";   // Keep this line!
            var a = 1;
            var aPrivateMethod = function() {
                var b = 2;
                alert(b);
            };
            this.aPublicMethod = function() {
                var c = 3;
                alert(c);
            };
        }
        
        addJS_Node (aClass);
        addJS_Node (
              'var aClass = new aClass();'
            + 'aClass.aPublicMethod();'
        );
        

答案 1 :(得分:1)

@BrockAdams Why is CDATA needed and not working everywhere the same way?的所有权利,想要在函数中编写所有内容并将内容插入脚本标记,谢谢。

感谢@polygenelubricants javascript get function body仅获取main()函数内容而不是函数及其内容。

var script = document.createElement("script");
function main()
{
var aClass = new AClass();
function AClass()
{
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();
}
var entire = main.toString();
var body = entire.substring(entire.indexOf("{") + 2, entire.lastIndexOf("}"));
script.textContent = body;
document.getElementsByTagName("head")[0].appendChild(script);

“+ 2”之后需要entire.indexOf("{"),以便不选择“{”字符,并且在脚本标记内容的开头没有空白的新行。

Javascript类函数不应该与我们保存类实例的变量同名,在Firefox aClass.aPublicMethod();中只会在你第一次执行代码时自动调用(你不能覆盖类函数) ),Chrome不关心符号,它有效。

所以,我选择了AClass作为类名,使用了aClass作为类对象。

奇怪的是,当我们使用.toString()方法时,Firefox似乎正在以他喜欢的方式解析/显示函数,例如,当插入脚本时,Firefox Inspector中的内容将仅显示在一行中。 Firebug将在每个新行之前用“tab”空格格式化代码,即使我们在插入main()函数时删除了空格(由于Firebug知道在substring之前有main()函数,所以它添加了自动空间)。 Firebug在AClass()函数之前和之后都添加了一个新行;当函数返回值保存在变量中时,如aPrivateMethod或aPublicMethod,它显示在1行。 Firebug的另一个奇怪变化是var aClass = new AClass();变为var aClass = new AClass;

function AClass()
{
    // code
}

变为

function AClass() {
    // code
}

依旧......

Chrome控制台始终尊重您在main()函数中编写的空格/新行。

我认为这是调试控制台的创建方式,并不重要。顺便说一句,在使用E4X扩展时,我所谈到的所有这些修改都不会出现在Firebug中。

使用function.toString()方法而不是E4X扩展时看到差异很有趣。也许有人有解释:))

使用function.toString()方法的Firefox控制台结果:

var aClass = new AClass; function AClass() { var a = 1; var aPrivateMethod = function () {var b = 2;alert(b);}; this.aPublicMethod = function () {var c = 34;alert(c);}; } aClass.aPublicMethod();

使用E4X扩展程序的Firefox控制台结果:

var aClass = new AClass(); function AClass() { var a = 1; var aPrivateMethod = function () { var b = 2; alert(b); }; this.aPublicMethod = function () { var c = 34; alert(c); }; } aClass.aPublicMethod();

使用function.toString()方法的Firebug结果:

    var aClass = new AClass;

    function AClass() {
        var a = 1;
        var aPrivateMethod = function () {var b = 2;alert(b);};
        this.aPublicMethod = function () {var c = 3;alert(c);};
    }

    aClass.aPublicMethod();

使用E4X扩展程序的Firebug结果和使用function.toString()方法的Chrome控制台结果(不支持E4X)是相同的:

var aClass = new aClass();
function aClass() {
    var a = 1;
    var aPrivateMethod = function() {
        var b = 2;
        alert(b);
    };
    this.aPublicMethod = function() {
        var c = 3;
        alert(c);
    };
}
aClass.aPublicMethod();

可以说是相关的,E4X是一种干净的方式,没有子串黑客但只支持Firefox:Creating multiline strings in JavaScript