DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> Mozilla的JavaScript引擎__noSuchMethod__()方法
Mozilla的JavaScript引擎__noSuchMethod__()方法
編輯:關於JavaScript     

JavaScript中有很多內部屬性和方法,在大多數情況下,只有JavaScript引擎才可以訪問,但不論什麼都是有特例的,在這裡就是指Mozilla的JavaScript引擎,包括SpiderMonkey和Rhino,都提供了若干接口來訪問這些內部屬性,如果加以合理利用的話,不僅可以讓JavaScript更加健壯,還可以開發出一些有意思的功能,比如本文介紹的__noSuchMethod__()方法就是其中之一。

著作權聲明

本文譯自 Nicholas C. Zakas 於2009年2月17日在個人網站上發表的《Mozilla JavaScript extension: __noSuchMethod__》。原文是唯一的正式版,本文是經過原作者(Nicholas C. Zakas)授權的簡體中文翻譯版(Simplified Chinese Translation)。譯者(明達)在翻譯的准確性上做了大量的努力,並承諾譯文的內容完全忠於原文,但可能還是包含疏漏和不妥之處,歡迎大家指正。譯注的內容是非正式的,僅代表譯者個人觀點。

以下是對原文的翻譯:

和其它浏覽器相比,Mozilla的JavaScript引擎總會有一些與眾不同的亮點。SpiderMonkey和他的好搭檔Rhino(用 Java實現的JavaScript引擎)都提供了很多擴展特性,可以幫助我們開發更加健壯的JavaScript應用。其中之一就是本地對象的__noSuchMethod__()方法。在大多數JavaScript引擎中,訪問一個不存在的方法時都只會簡單的拋出錯誤,而在Mozilla的引擎中,這只是默認行為,我們可以通過覆蓋某個對象的__noSuchMethod__()方來來重新定義這個行為。當這個對象試圖調用一個不存在的方法時,就會觸發該對象的__noSuchMethod__()方法。

當__noSuchMethod__()方法被調用時,JavaScript引擎會傳入兩個參數,一個是調用方法的名稱,一個是參數數組,對應於要傳遞給調用方法的所有參數,注意這個參數數組應該是一個Array對象,而不是arguments對象,而且就算沒有參數,也要傳遞一個空數組,下面舉一個簡單的例子加以說明:

// 注意,下面的代碼只有在使用SpiderMonkey或者Rhino的浏覽器中才會被正確解析
var person = {
    name: "Nicholas",
    __noSuchMethod__: function(name, args){
        alert("Method called '" + name +
            "' executed with arguments [" + args + "]");
    }
}
//"Method called 'sayName' executed with arguments []"
person.sayName();      
//"Method called 'phone' executed with arguments [Mike]"
person.phone("Mike");

 這段代碼定義了一個person對象,並重寫了該對象的__noSuchMethod__()方法。當調用person對象的sayName()方法和phone()方法時,由於這兩個方法都不存在,所以__noSuchMethod__()方法會被自動調用,這樣我們就避免了一個錯誤的顯示,並且可以做出相應的處理。在上面這個例子中,我們所做的處理就是直接將方法的名稱和傳遞的參數直接顯示出來。

但話說回來,在運行時才發現未定義方法的情況,是不該出現在正常的開發實踐中的,那簡直就是自取煩惱。對於通常的情況來說,這個方法似乎沒有什麼作用,但他確實為我們提供了一個可能性,來創建一些有趣的動態工具,比如說建立一個可以輸出有效XHTML文檔的對象:

function HTMLWriter(){
    this._work = [];
}
HTMLWriter.prototype = {
    escape: function (text){
        return text.replace(/[><"&]/g, function(c){
            switch(c){
                case ">": return ">";
                case "<": return "<";
                case "\"": return """;
                case "&": return "&";
            }
        });
    },
    startTag: function(tagName, attributes){
        this._work.push("<" + tagName);
        if (attributes){
            var name, value;
            for (name in attributes){
                if (attributes.hasOwnProperty(name)){
                    value = this.escape(attributes[name]);
                    this._work.push(" " + name + "=\"" + value + "\"");
                }
            }
        }
        this._work.push(">");
    },
    text: function(text){
        this._work.push(this.escape(text));
    },
    endTag: function(tagName){
        this._work.push("");
    },
    toString: function(){
        return this._work.join("");
    }
};
var writer = new HTMLWriter();
writer.startTag("html");
writer.startTag("head");
writer.startTag("title");
writer.text("Example & Test");
writer.endTag("title");
writer.endTag("head");
writer.startTag("body", { style: "background-color: red" });
writer.text("Hello world!");
writer.endTag("body");
writer.endTag("html");
alert(writer);

這段代碼通過三個方法來完成任務,分別是:startTag()、endTag()和text()。而上面給出的調用例子,卻顯得非常冗長。想象一下,如果不使用startTag()、endTag()和text()這三個方法,而是為每一個有效的XHTML標記都建立一個方法,那麼這個例子的調用方法可能就會變成下面這個樣子了:

var writer = new HTMLWriter();
var result = writer.html()
    .head().title().text("Example & Test").xtitle().xhead()
    .body().text("Hell world!").xbody()
.xhtml().toString();

由於每個標簽的實現大致相同,所以我們可能需要為HTMLWriter對象復制一系列非常相似的方法,這無疑是一種嚴重的浪費行為。而這正是__noSuchMethod__()發揮真正作用的時候。讓我們來看看用__noSuchMethod__()來實現這種效果到底有多麼簡單:

function HTMLWriter(){
    this._work = [];
}
HTMLWriter.prototype = {
    escape: function (text){
        return text.replace(/[><"&]/g, function(c){
            switch(c){
                case ">": return ">";
                case "<": return "<";
                case "\"": return """;
                case "&": return "&";
            }
        });
    },
    text: function(text){
        this._work.push(this.escape(text));
        return this;
    },
    toString: function(){
        return this._work.join("");
    },
    __noSuchMethod__: function(name, args){
        var tags = [
            "a", "abbr", "acronym", "address", "applet", "area",
            "b", "base", "basefont", "bdo", "big", "blockquote",
            "body", "br", "button",
            "caption", "center", "cite", "code", "col", "colgroup",
            "dd", "del", "dir", "div", "dfn", "dl", "dt",
            "em",
            "fieldset", "font", "form", "frame", "frameset",
            "h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "html",
            "i", "iframe", "img", "input", "ins", "isindex",
            "kbd",
            "label", "legend", "li", "link",
            "map", "menu", "meta",
            "noframes", "noscript",
            "object", "ol", "optgroup", "option",
            "p", "param", "pre",
            "q",
            "s", "samp", "script", "select", "small", "span", "strike",
            "strong", "style", "sub", "sup",
            "table", "tbody", "td", "textarea", "tfoot", "th", "thead",
            "title", "tr", "tt",
            "u", "ul",
            "var"
        ];
        var closeTag = (name.charAt(0) == "x"),
            tagName = closeTag ? name.substring(1) : name;
        if (tags.indexOf(tagName) > -1){
            if (!closeTag){
                this._work.push("<" + tagName);
                if (args.length){
                    var attributes = args[0],
                        name, value;
                    for (name in attributes){
                        if (attributes.hasOwnProperty(name)){
                            value = this.escape(attributes[name]);
                            this._work.push(" " + name + "=\"" +
                                 value + "\"");
                        }
                    }
                }
                this._work.push(">");
            } else {
                this._work.push("");
            }
            return this;
        } else {
            throw new Error("Method '" + name + "' is undefined.");
        }
    }
};

這段代碼的主要功能都是在__noSuchMethod__()中實現的。它包含一個數組,對應於全部有效的XHTML標簽,用於查找可以調用的方法。如果需要關閉標簽,只需要在方法名前面加一個“x”就可以,在__noSuchMethod__()中,會對方法名的首字母進行判斷,如果首字母是“x”,就會標記為結束標簽,並將“x”從方法名稱中去掉。接下來,會通過Mozilla的數組擴展 indexOf()來判斷方法名稱是否在可用標簽的數組裡面,如果方法名是無效的,就會拋出一個錯誤;如果方法名是有效的,就會返回生成的標簽字符串。代碼所支持的標簽數量是可以動態設置的,我們只需要對標簽列表數組進行修改,就可以達到添加或者刪除“方法”的目的。

很顯然,由於這個特性不能跨浏覽器,所以肯定不能用於通常的情況下。但對於那些專門針對Mozilla引擎(比如Firefox等)的JavaScript應用來說,這無疑為我們敞開了一道神奇的大門,尤其在開發動態接口時,__noSuchMethod__()將是一個非常強有力的工具。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved