`
houzhe11
  • 浏览: 141721 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

Thinking in JavaScript Meta-Programming

阅读更多
Meta-Programming是一个比较广义的概念,你可以将它翻译成元类或者元模型编程,而它实际的意思,是用一系列方法生成类型模板。在 JavaScript,所谓的类型模板就是function,而元类,就是Function和由Function引出的一系列扩展。

关于JavaScript的Meta-Programming思想,其实有很多实现的例子和各种不同的方法,而在本文中,将它们归结为三个范式。所谓范式,你可以将它们理解为一些“公式化”的概念,或者某种模式,当你遇到同一类问题的时候,你应该寻找适合解决此类问题的“范式”。

第一范式:<new> T <=> <new> R:function(){donothing, return T.apply},R.prototype = T.prototype

这个范式被称为函数范式,它有如上面所列的标准形式和其他几种变形。标准形式被称为“前束”范式,因为它能够以语法等价的形式在函数T执行前插入一段代码。例如:

[复制]
Code:

Function.prototype.$verify = function(){  //对函数进行参数类型匹配

    var me = this;
    var _args = arguments;
    var mins = function(){
        for(var j= 0, len = _args.length; j< len; j++)
        {
            if(!$oneof(arguments[j],_args[j])){
                    throw new Error("函数的参数类型不匹配,位置:"+(j+1));
            }
        }
        return me.apply(this, arguments);
    }
    mins.prototype = me.prototype;
    return mins;   
}


上面的代码对函数增加了参数类型匹配的扩展,它可以实现强制解释器对某个函数在调用之前进行参数类型匹配。例如:

[复制]
Code:

var foo = function(x,y){
    alert(x+y);
}.$verify("number","number");

foo(1,2);

var foo2 = function(x,y){
    x(y);
}.$verify(Function,"number");
//foo("error",2);

foo2(function(x){alert(x)},10);
//foo2("x","y");

var Class3 = function(x,y){
    this.x = x;
    this.y = y;
}.$verify("number","number");
Class3.prototype.dist2 = function(){return this.x*this.x + this.y*this.y};
var c = new Class3(10,20);
alert(c.dist2());


注意到上面这一段代码,$verify返回一个function,这个function是调用$verify的那个 function的一个第一范式迭代,这种处理方法在语法层面上达到很好的效果,而且它是无害的,这意味着你在编写和调试代码的时候可以得到$verify带来的好处,而在你发布代码的时候,你却可以很容易地用文本处理工具将$verify “尾巴”从你的代码中移出出去。

除此以外,第一范式的应用是很广泛的,因为它意味着你可以对函数进行任意扩展并且这些扩展不改变代码本身的语法结构!
你可以给函数增加某些不同功能的“尾巴”这些尾巴能够很好地帮你收集运行时信息、监视代码或者提供有用的调试信息。而这些所有的“尾巴”在你最终发布代码时,均可以非常方便地去掉,所以它们对实际运行的代码不会带来任何性能上的开销!这一点,对于开发者来说,无疑是非常非常好的消息!

第二范式:new T <=> T.apply(T.getPrototypeObject())

在几个月或者一年以前,我就在思考一个问题,我们知道,对于一个function T,脚本既可以把它当作一个方法来执行,又可以把它作为一个类型来构造,然而它们是不同的。除了new之外,其中语法上最显著的一个区别是T作为一个 function,既可以用()来直接操作,也可以享受call和apply带来的好处。而new,则受到比较大的限制,例如:

[复制]
Code:

function List()
{
    this.members = Array.prototype.slice.apply(arguments); //Array支持可变参数,因为它可以作为函数来调用
}
function $list()
{
    return new List(/*这里的参数应该怎么传?如果我希望$list(1,2,3...) <=> new List(1,2,3...)*/);
}


我们看到,相对function来讲,new操作受到较大的限制,当然上面这个实际问题是可以通过别的方式来解决的,但是用第二范式,无疑可以具有通用性地解决此类问题:

[复制]
Code:

G.objectAsPrototype = function(obj, c){
    c = c || function(){};
    c.prototype = obj;
    return c;
};
Function.prototype.getPrototypeObject = function(){
    var p = this.__templete__ || (this.__templete__= G.objectAsPrototype(this.prototype));
    return new p();
};
Function.prototype.createInstance = function(){
    var p = this.getPrototypeObject();
    this.apply(p,arguments);
    return p;
};


我们看到,第二范式解决了这样的问题,它告诉我们,JavaScript的new T操作等价于通过T的prototype创造一个“原型”对象,再用这个对象去执行T的构造函数,最终等价于产生了一个T的实例,但区别是后者不同于 new,是以一种函数调用的标准形式来产生的。

利用第二范式,我们可以很容易地增强JavaScript的原型继承,轻易地解决原型继承中关于构造函数延迟执行的需求(具体的将在另外一篇文章《深度探索高效率JavaScript继承》给出详细说明)
下面给出简单代码:

[复制]
Code:

Function.prototype.$pextends = function(p){
    var me = this;
    var ins = function()
    {
        this.$super = function(){
            p.apply(this, arguments);
        }
        me.apply(this, arguments);
    }

    ins.prototype = p.getPrototypeObject();
    return ins;
}


第三范式:new T <=> T.apply || new T(T.apply)

记得之前有人问过我一个有意思的问题,是关于js核心对象的扩展的。那个需求是实现一个自定义的Date类型MyDate,并且这个MyDate的所有构造参数都要和Date完全一致。之前,这个问题在解决的时候遇到一个困扰,具体的是这样的:

[复制]
Code:

function MyDate()
{
    var ins = new Date(/*原生对象的扩展方式,可是如何处理可变参数呢?*/);
    ......
    return ins;
}


而第三范式的意思是说,对于所有的核心对象,都满足如下两种情况之一:要么函数调用的返回结果等效于new操作,要么函数调用的返回结果利用new构造后等同于原始的new操作。Date的问题满足后者,也就是说,对于这个问题,将上面的代码改写成如下:

[复制]
Code:

function MyDate()
{
    var ins = new Date(Date.apply(this, arguments));
    ......
    return ins;
}


即可满足需求。
显然,这个第三范式并不是一个JavaScript类型默认遵循的范式,不过,有趣的是,几乎所有的核心对象都遵循第三范式:

Array、Function 满足范式右侧的第一个条件
Number、Boolean、String、Date、RegExp 满足范式右侧的第二个条件

所以,利用第三范式,我们可以实现核心对象的继承方法(关于核心对象继承的详细内容也会在《深度探索高效率JavaScript继承》给出详细讨论):

[复制]
Code:

Function.prototype.$cextends = function(p){
    var me = this;
    return function()
    {
        var ins = p.apply(this, arguments);
        ins instanceof p || (ins = new p(ins));

        me.apply(ins,arguments);
        return ins;
    }
}


除了继承核心对象之外,第三范式还有其他很有趣的应用:

[复制]
Code:

var MyFunction = function(){
    this.m = function(){alert("static m")}
    this.prototype.m = function(){alert("m")};
}.$cextends(Function);

//简单实现的Function Template
var X = new MyFunction("alert(1)");
var x = new X();
X.m();
x.m();


上面这段代码实现了一个自定义的函数模版,这样使用者就能够很方便地自己扩展Function,这个模式的意义是让Function元类具备有扩展能力,这种能力正是Meta-Programming所需要的。
分享到:
评论

相关推荐

    重温 Thinking in Java 5 - The Class object

    《Thinking in Java》是Bruce Eckel的经典之作,第五版(The Class object)主要探讨的是类对象(Class object)这一核心概念。在Java编程语言中,类对象是与类相关的特殊对象,它提供了对类信息的访问,包括类的元...

    Thinking in C++习题答案

    《Thinking in C++》是一本深受程序员喜爱的C++学习指南,由Bruce Eckel编写,旨在帮助读者深入理解和掌握C++编程语言。该书分为两卷,第一卷主要介绍了C++的基础知识,包括面向对象编程的基本概念,而第二卷则更...

    thinking in java2-3

    《Thinking in Java》是Bruce Eckel的经典之作,它深入浅出地介绍了Java编程语言的核心概念和技术。本压缩包文件的标题“thinking in java2-3”表明内容涵盖了该书的第二部分,通常包括面向对象编程的深入探讨,可能...

    thinking in java3-3

    《Thinking in Java》是Java编程领域的一本经典著作,由Bruce Eckel撰写,被誉为学习Java的必读之作。这本书深入浅出地介绍了Java语言的核心概念和技术,为初学者提供了全面而扎实的学习路径。"Thinking in Java 3-3...

    thinking-in-cpp-2.zip

    本压缩包文件"thinking-in-cpp-2.zip"包含了第二卷的内容,为学习C++的开发者提供了宝贵的资源。 第一卷主要涵盖了C++的基础知识,包括基本语法、数据类型、控制结构、函数、类和对象等主题。其中,"thinking"这一...

    Thinking In Java-Java 编程思想(中英文版 第四版)

    Thinking In Java-Java 编程思想(中英文版 第四版) Thinking In Java-Java 编程思想(中英文版 第四版)

    Thinking-in-Patterns-with-Java.zip_in

    《Thinking in Patterns with Java》是一本深度探讨Java编程思想与设计模式的经典著作,旨在帮助开发者理解和运用各种设计模式,从而提升软件开发的效率和质量。这本书深入浅出地讲解了如何在Java环境中实现和应用...

    thinking in java1-3

    《Thinking in Java》是Java编程领域的一本经典著作,由Bruce Eckel撰写,深受程序员喜爱。这本书深入浅出地介绍了Java语言的基础知识和高级特性,是初学者和进阶者的重要参考材料。从提供的信息来看,你可能已经...

    Thinking in Java4-课程代码code

    《Thinking in Java 4》是 Bruce Eckel 编著的一本经典Java编程教材,它深入浅出地介绍了Java语言的核心概念和技术。这个压缩包“Thinkinjava4-code”包含了该书第四版对应的课程代码,是学习和理解书中理论知识的...

    C++编程思想-Thinking in C++ 11

    C++编程思想-Thinking in C++

    Thinking-in-Java-4th-Edition习题答案

    总的来说,这份“Thinking-in-Java-4th-Edition习题答案”涵盖了Java编程的广泛领域,从基本语法到高级特性,从理论到实践,是提高Java技能的宝贵资源。通过解决这些习题,你不仅可以巩固所学知识,还能锻炼解决问题...

    TIJ4-solutions.pdf(thinking in java 4th 官方答案,全)

    这个是我花钱买的,现在献给大家......

    《Thinking in C++》pdf电子版

    《Thinking in C++》是一本深受程序员喜爱的经典C++教程,由Bruce Eckel撰写,旨在帮助读者深入理解和掌握C++编程语言。这本书以其全面、深入和实用的特点,被广泛视为学习C++的必备参考书之一。《C++编程思想》是其...

    thinking_in_java-master.zip_Thinking in Java_zip

    《Thinking in Java》是Bruce Eckel的经典Java编程书籍,它为初学者和有经验的程序员提供了深入理解Java语言的全面指南。这本书强调了面向对象编程的概念,并通过丰富的实例来解释复杂的概念,使得学习过程更为直观...

    thinking-in-java-侯捷翻译版

    《Thinking in Java》是由Bruce Eckel编著,侯捷翻译的Java编程经典教程。这本书深入浅出地介绍了Java语言的核心概念和技术,对于学习和理解Java编程有着极高的价值。在这个侯捷翻译的版本中,作者以清晰易懂的语言...

    Thinking In Java- JAVA思考 PDF版

    Thinking In Java- JAVA思考pdf版

    第四版-Thinking+In+Java-练习题答案

    《Thinking in Java》是Bruce Eckel的经典编程教材,第四版更是深受广大Java程序员喜爱。这本书以其深入浅出的讲解方式,全面系统地介绍了Java语言的核心概念和技术。在解答该书的练习题时,我们可以深入理解Java...

    Computation-Thinking-and-Programming-Design:运算思维与程式设计

    在“Computation-Thinking-and-Programming-Design-main”这个文件夹中,我们可以期待找到关于如何应用运算思维进行编程的实例和练习。可能包含的资源有: - Jupyter Notebook文件,其中包含了示例代码和逐步解释,...

    Thinking in Java 4th and Source Code

    《Thinking in Java》是Bruce Eckel的经典之作,第四版更是被广大Java开发者视为学习和进阶的必备书籍。这本书深入浅出地介绍了Java语言的核心概念和技术,包括面向对象编程、集合框架、多线程、网络编程、异常处理...

    Thinking in java edition3-code

    《Thinking in Java》是Bruce Eckel的经典编程教材,第三版(edition3)深入浅出地介绍了Java编程语言的核心概念和技术。这个压缩包文件包含了书中所提及的示例代码,名为"TIJcode",这对于理解和实践书中理论知识至...

Global site tag (gtag.js) - Google Analytics