Mozilla专有JavaScript扩展之一(__noSuchMethod__)

作者:明达 来源:七月佑安 时间:2009-03-01 12:45:00 

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__()将是一个非常强有力的工具。

标签:Mozilla,扩展,JavaScript,属性,noSuchMethod
0
投稿

猜你喜欢

  • 带你轻松接触MySQL数据库的出错代码列表

    2008-12-31 15:06:00
  • 网页设计十大诀窍

    2007-10-19 13:03:00
  • asp如何选择访问速度最快的站点?

    2010-06-10 18:34:00
  • Oracle数据库快照的使用

    2010-07-28 13:32:00
  • asp sql去左右空格函数

    2008-03-04 17:29:00
  • sp_executesql 使用复杂的Unicode 表达式错误的解决方法

    2012-01-29 17:58:52
  • 让网站容易被发现

    2009-04-20 20:35:00
  • PS中执行N遍选定动作的脚本

    2008-02-22 21:33:00
  • Alexa排名数据xml接口及其参数说明

    2008-11-07 13:03:00
  • JavaScript创建可维护幻灯片效果

    2008-06-21 16:42:00
  • SQL Server和MySql中创建临时表

    2008-06-19 13:31:00
  • 小技巧解决“FF不能读取outerHTML”的问题

    2009-02-10 10:44:00
  • Google中秋logo被恶搞

    2007-09-25 15:55:00
  • 随感:交互设计两三事

    2009-05-18 13:54:00
  • 用javascript实现select的美化

    2007-05-11 16:50:00
  • 优化MySQL的数据库性能的八种方法

    2012-01-05 19:28:53
  • SQL Server 2005中的CLR集成

    2009-03-10 15:07:00
  • 极简网页设计视觉呈现技巧

    2010-04-20 14:55:00
  • asp使用jmail4.3的模块

    2010-03-17 20:58:00
  • 在sql Server自定义一个用户定义星期函数

    2012-02-12 15:47:28
  • asp之家 网络编程 m.aspxhome.com