这段Javascript代码是做什么的?

17

我一直在查看SharePoint脚本文件,遇到了这段代码,但是我不太明白:

function ULSTYE() {
    var o = new Object;
    o.ULSTeamName = "Microsoft SharePoint Foundation";
    o.ULSFileName = "SP.UI.Dialog.debug.js";

    return o;
}

SP.UI.$create_DialogOptions = function() {
    ULSTYE:;   <----------------------------- WTF?
    return new SP.UI.DialogOptions();
}

实际上,该文件中的每个函数定义都以相同的ULSTYE:;行开始,紧跟着左大括号。有人能解释一下第二个函数的第一行是做什么的吗?

例如,Firefox/Firebug将此函数解释为我也无法理解的东西:

function () {
    ULSTYE: {
    }
    return new (SP.UI.DialogOptions);
}

我曾以为自己对JavaScript非常了解... ;) 可能是我过去从未使用过的某些晦涩特性,显然其他人也很少使用。

4个回答

21

经过长时间的思考,我终于坐下来研究了一下这个问题。这是一个相对复杂的机制,用于在客户端收集诊断信息,包括能够将Javascript调用堆栈(包括函数名和Javascript文件)发送回服务器的功能。

请查看位于

%Program Files%\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\1033\init.debug.js

前250行代码,该文件定义了客户端上'ULS'实现的所有函数。

当然,您需要安装SharePoint 2010,才能在本地计算机上找到该文件。

更新--以下大致介绍了机制的运作方式。实际实现还要更多。

考虑以下具有几个js引用的HTML页面,每个引用都可以被其他引用调用。

<html>
 <head>
   <script type="text/javascript" src="ErrorHandling.js"></script>
   <script type="text/javascript" src="File1.js"></script>
   <script type="text/javascript" src="File2.js"></script>
 </head>
 <body>
   <button onclick="DoStuff()">Do stuff</button>
 </body>
</html>

我们有两个js包含文件,File1.js

    function ULSabc() { var o = new Object; o.File = "File1.js"; return o; }
    /* ULSabc is the unique label for this js file. Each function in 
    this file can be decorated with a label corresponding with the same name */

    function DoStuff() {
        ULSabc: ;
        //label matches name of function above
        DoMoreStuff();
    }

和 File2.js

    function ULSdef() { var o = new Object; o.File = "File2.js"; return o; }

    function DoMoreStuff() {
        ULSdef: ;
        DoEvenMoreStuff();
    }

    function DoEvenMoreStuff() {
        ULSdef: ;
        try {
            //throw an error
            throw "Testing";
        } catch (e) {
            //handle the error by displaying the callstack
            DisplayCallStack(e);
        }
    }

现在,假设我们的ErrorHandling文件看起来像这样

    function GetFunctionInfo(fn) {
        var info = "";
        if (fn) {
            //if we have a function, convert it to a string
            var fnTxt = fn.toString();

            //find the name of the function by removing the 'function' and ()
            var fnName = fnTxt.substring(0, fnTxt.indexOf("(")).substring(8);
            info += "Function: " + fnName;

            //next use a regular expression to find a match for 'ULS???:' 
            //which is the label within the function
            var match = fnTxt.match(/ULS[^\s;]*:/);
            if (match) {
                var ULSLabel = match[0];

                //if our function definition contains a label, strip off the 
                // : and add () to make it into a function we can call eval on
                ULSLabel = ULSLabel.substring(0, ULSLabel.length - 1) + "()";

                //eval our function that is defined at the top of our js file
                var fileInfo = eval(ULSLabel);
                if (fileInfo && fileInfo.File) {
                 //add the .File property of the returned object to the info
                    info += " => Script file: " + fileInfo.File;
                }
            }
        }
        return info;
    }

    function DisplayCallStack(e) {
        //first get a reference to the function that call this
        var caller = DisplayCallStack.caller;
        var stack = "Error! " + e + "\r\n";

        //recursively loop through the caller of each function,
        //collecting the function name and script file as we go
        while (caller) {
            stack += GetFunctionInfo(caller) + "\r\n";
            caller = caller.caller;
        }

        //alert the callstack, but we could alternately do something 
        //else like send the info to the server via XmlHttp.
        alert(stack);
    }

当我们在页面上点击按钮时,我们的脚本文件将通过每个函数进行调用,并在DisplayCallStack结束,此时它将递归循环并收集堆栈跟踪

    Error! Testing
    Function: DoEvenMoreStuff => Script file: File2.js
    Function: DoMoreStuff     => Script file: File2.js
    Function: DoStuff         => Script file: File1.js
    Function: onclick

1
也许您可以解释一下为什么这个“复杂”的机制需要一个完全无用的标签,以及它是如何使用的。仅仅引用一个相对较少人拥有的文件并说“阅读此文件”并不是很有帮助。 - T.J. Crowder
当然...我不会发布来自微软的JS代码,但我会添加一个简化的示例,展示它的基本工作原理。 - Paul Lucas
1
所以你的意思是它依赖于在函数上使用toString(这从未被标准化,并且在某些移动浏览器上不起作用),并且它依赖于该函数的字符串表示形式没有被优化删除无用标签。有趣,我想我会选择另一种方式。尽管如此,回答很好,+1。 - T.J. Crowder
1
谢谢。最后需要注意的是,实际实现将尽其所能收集浏览器可以提供的任何信息,并在必要时进行优雅降级。请记住,这是SharePoint代码,因此最适合使用Internet Explorer。 - Paul Lucas

14

第一部分定义了一个函数,创建了一个带有几个属性的对象并返回它。我认为我们都很清楚这一点。

然而,第二部分没有使用该函数。它定义了一个与该函数同名的标签。尽管它使用相同的字符序列,但它并不是对上面的函数的引用。Firefox的解释与任何其他解释一样有道理,因为标签应该紧跟着可以引用的内容。

有关标记语句的更多信息,请参见规范第12.12节。


离题:我建议避免使用这个源代码。编写它的人显然对JavaScript比较新,并没有显示出他们知道自己在做什么的迹象。例如,他们在new Object()调用中省略了(),虽然这是允许的,但这是相当危险的事情。他们可能会争辩说他们这样做是为了节省空间,但如果他们这样做,最好使用对象字面量:

function ULSTYE() {
    return {
        ULSTeamName: "Microsoft SharePoint Foundation",
        ULSFileName: "SP.UI.Dialog.debug.js"
    };
}

几乎没有理由编写 new Object(){} 的功能完全相同。

当然,第二部分完全没有道理。:-)


6
不会是微软开发者吧? :) - Marko
1
我会避免使用来自这个来源的代码。编写它的人显然是相当新的JavaScript开发者,并且没有表现出他们知道自己在做什么的迹象。告诉微软公司!有趣的是,这个脚本是由微软员工编写的,是Sharepoint Foundation 2010的一部分。 :) 这是SP.UI.Dialog.Debug.js文件的第一个部分... 我不认为他们是没有经验的。 ;) - Robert Koritnik
5
@Robert:微软整体可能不缺乏经验,但请不要认为他们没有经验不足的开发人员,有些问题可能会从中渗透出来。上述引用的代码可能是由某个不知所措的人编写的,由一个略微疯狂的自动生成器创建(也许输入无效),或者(当然!)它是我不熟悉的构造。我对JavaScript有很多了解,但并不是全部都知道 :-) 例如,在函数顶部只有 "use strict"; 的一行看起来是毫无意义的,如果您不知道严格模式的话。 - T.J. Crowder
1
@Robert:关于标签作用域的问题,这是一个好问题。这在12.12节中有所涉及,但语言过于生硬,很难理解它的含义。基本上,标签的作用域限定在它们所标记的语句及其内容中。每个语句都从一个空标签集开始,如果标签标记了该语句,则将标签添加到集合中。由于您可以嵌套语句(例如for语句中的switch语句),因此可能会在集合中得到多个标签,但一旦您退出语句的标签,则该标签就不再存在了。例如:http://jsbin.com/emavi4 第二个循环无法看到第一个循环的标签。 - T.J. Crowder
1
@pst:在调用SP.UI.$createDialogOptions()之前,您需要等待sp.dialog.js加载完成。大多数SharePoint JavaScript是“按需”加载的,以提高页面加载性能。虽然这是一个很好的想法,但您还需要了解框架才能调用框架函数。 - Paul Lucas
显示剩余6条评论

4

这不就是一个语句标签吗?我认为标签与之前的函数名称相同并没有任何意义。


0

看起来,它创建了一个空对象,应该填充一些数据,但由于生成此代码的代码生成器未被删除,因此它保持为空。


创建一个对象意味着使用赋值运算符(=),但它并不存在(冒号存在)。此外,在任何东西的末尾缺少分号... - Robert Koritnik

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