`
aidxn527
  • 浏览: 43387 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类

悟透JavaScript (2)

    博客分类:
  • JS
阅读更多

在JavaScript内部,对象的属性和方法追溯机制是通过所谓的prototype链来实现的。当用new操作符构造对象时,也会同时将构造函数的 prototype对象指派给新创建的对象,成为该对象内置的原型对象。对象内置的原型对象应该是对外不可见的,尽管有些浏览器(如Firefox)可以让我们访问这个内置原型对象,但并不建议这样做。内置的原型对象本身也是对象,也有自己关联的原型对象,这样就形成了所谓的原型链。

    在原型链的最末端,就是Object构造函数prototype属性指向的那一个原型对象。这个原型对象是所有对象的最老祖先,这个老祖宗实现了诸如 toString等所有对象天生就该具有的方法。其他内置构造函数,如Function, Boolean, String, Date和RegExp等的prototype都是从这个老祖宗传承下来的,但他们各自又定义了自身的属性和方法,从而他们的子孙就表现出各自宗族的那些特征。

    这不就是“继承”吗?是的,这就是“继承”,是JavaScript特有的“原型继承”。

    “原型继承”是慈祥而又严厉的。原形对象将自己的属性和方法无私地贡献给孩子们使用,也并不强迫孩子们必须遵从,允许一些顽皮孩子按自己的兴趣和爱好独立行事。从这点上看,原型对象是一位慈祥的母亲。然而,任何一个孩子虽然可以我行我素,但却不能动原型对象既有的财产,因为那可能会影响到其他孩子的利益。从这一点上看,原型对象又象一位严厉的父亲。我们来看看下面的代码就可以理解这个意思了:

Js代码 复制代码
  1. function Person(name)   
  2. {   
  3.     this.name = name;   
  4. };   
  5.   
  6. Person.prototype.company = "Microsoft"//原型的属性   
  7.   
  8. Person.prototype.SayHello = function()  //原型的方法   
  9. {   
  10.     alert("Hello, I'm " + this.name + " of " + this.company);   
  11. };   
  12.   
  13. var BillGates = new Person("Bill Gates");   
  14. BillGates.SayHello();   //由于继承了原型的东西,规规矩矩输出:Hello, I'm Bill Gates   
  15.   
  16. var SteveJobs = new Person("Steve Jobs");   
  17. SteveJobs.company = "Apple";    //设置自己的company属性,掩盖了原型的company属性   
  18. SteveJobs.SayHello = function() //实现了自己的SayHello方法,掩盖了原型的SayHello方法   
  19. {   
  20.     alert("Hi, " + this.name + " like " + this.company + ", ha ha ha ");   
  21. };   
  22.   
  23. SteveJobs.SayHello();   //都是自己覆盖的属性和方法,输出:Hi, Steve Jobs like Apple, ha ha ha    
  24.   
  25. BillGates.SayHello();   //SteveJobs的覆盖没有影响原型对象,BillGates还是按老样子输出  
    function Person(name)
    {
        this.name = name;
    };
    
    Person.prototype.company = "Microsoft"; //原型的属性
    
    Person.prototype.SayHello = function()  //原型的方法
    {
        alert("Hello, I'm " + this.name + " of " + this.company);
    };
    
    var BillGates = new Person("Bill Gates");
    BillGates.SayHello();   //由于继承了原型的东西,规规矩矩输出:Hello, I'm Bill Gates
    
    var SteveJobs = new Person("Steve Jobs");
    SteveJobs.company = "Apple";    //设置自己的company属性,掩盖了原型的company属性
    SteveJobs.SayHello = function() //实现了自己的SayHello方法,掩盖了原型的SayHello方法
    {
        alert("Hi, " + this.name + " like " + this.company + ", ha ha ha ");
    };

    SteveJobs.SayHello();   //都是自己覆盖的属性和方法,输出:Hi, Steve Jobs like Apple, ha ha ha 
    
    BillGates.SayHello();   //SteveJobs的覆盖没有影响原型对象,BillGates还是按老样子输出



    对象可以掩盖原型对象的那些属性和方法,一个构造函数原型对象也可以掩盖上层构造函数原型对象既有的属性和方法。这种掩盖其实只是在对象自己身上创建了新的属性和方法,只不过这些属性和方法与原型对象的那些同名而已。JavaScript就是用这简单的掩盖机制实现了对象的“多态”性,与静态对象语言的虚函数和重载(override)概念不谋而合。

    然而,比静态对象语言更神奇的是,我们可以随时给原型对象动态添加新的属性和方法,从而动态地扩展基类的功能特性。这在静态对象语言中是很难想象的。我们来看下面的代码:

Js代码 复制代码
  1. function Person(name)   
  2. {   
  3.     this.name = name;   
  4. };   
  5.   
  6. Person.prototype.SayHello = function()  //建立对象前定义的方法   
  7. {   
  8.     alert("Hello, I'm " + this.name);   
  9. };   
  10.   
  11. var BillGates = new Person("Bill Gates");   //建立对象   
  12.   
  13. BillGates.SayHello();   
  14.   
  15. Person.prototype.Retire = function()    //建立对象后再动态扩展原型的方法   
  16. {   
  17.     alert("Poor " + this.name + ", bye bye!");   
  18. };   
  19.   
  20. BillGates.Retire(); //动态扩展的方法即可被先前建立的对象立即调用  
    function Person(name)
    {
        this.name = name;
    };
    
    Person.prototype.SayHello = function()  //建立对象前定义的方法
    {
        alert("Hello, I'm " + this.name);
    };
    
    var BillGates = new Person("Bill Gates");   //建立对象
    
    BillGates.SayHello();
    
    Person.prototype.Retire = function()    //建立对象后再动态扩展原型的方法
    {
        alert("Poor " + this.name + ", bye bye!");
    };
    
    BillGates.Retire(); //动态扩展的方法即可被先前建立的对象立即调用



    阿弥佗佛,原型继承竟然可以玩出有这样的法术!

原型扩展

    想必君的悟性极高,可能你会这样想:如果在JavaScript内置的那些如Object和Function等函数的prototype上添加些新的方法和属性,是不是就能扩展JavaScript的功能呢?

    那么,恭喜你,你得到了!

    在AJAX技术迅猛发展的今天,许多成功的AJAX项目的JavaScript运行库都大量扩展了内置函数的prototype功能。比如微软的 ASP.NET AJAX,就给这些内置函数及其prototype添加了大量的新特性,从而增强了JavaScript的功能。

    我们来看一段摘自MicrosoftAjax.debug.js中的代码:

Js代码 复制代码
  1. String.prototype.trim = function String$trim() {   
  2.     if (arguments.length !== 0) throw Error.parameterCount();   
  3.     return this.replace(/^\s+|\s+$/g, '');   
  4. }  
String.prototype.trim = function String$trim() {
    if (arguments.length !== 0) throw Error.parameterCount();
    return this.replace(/^\s+|\s+$/g, '');
}



    这段代码就是给内置String函数的prototype扩展了一个trim方法,于是所有的String类对象都有了trim方法了。有了这个扩展,今后要去除字符串两段的空白,就不用再分别处理了,因为任何字符串都有了这个扩展功能,只要调用即可,真的很方便。

    当然,几乎很少有人去给Object的prototype添加方法,因为那会影响到所有的对象,除非在你的架构中这种方法的确是所有对象都需要的。

    前两年,微软在设计AJAX类库的初期,用了一种被称为“闭包”(closure)的技术来模拟“类”。其大致模型如下:

Js代码 复制代码
  1. function Person(firstName, lastName, age)   
  2. {   
  3.     //私有变量:   
  4.     var _firstName = firstName;   
  5.     var _lastName = lastName;   
  6.   
  7.     //公共变量:   
  8.     this.age = age;   
  9.   
  10.     //方法:   
  11.     this.getName = function()   
  12.     {   
  13.         return(firstName + " " + lastName);   
  14.     };   
  15.     this.SayHello = function()   
  16.     {   
  17.         alert("Hello, I'm " + firstName + " " + lastName);   
  18.     };   
  19. };   
  20.   
  21. var BillGates = new Person("Bill""Gates", 53);   
  22. var SteveJobs = new Person("Steve""Jobs", 53);   
  23.   
  24. BillGates.SayHello();   
  25. SteveJobs.SayHello();   
  26. alert(BillGates.getName() + " " + BillGates.age);   
  27. alert(BillGates.firstName);     //这里不能访问到私有变量  
    function Person(firstName, lastName, age)
    {
        //私有变量:
        var _firstName = firstName;
        var _lastName = lastName;

        //公共变量:
        this.age = age;

        //方法:
        this.getName = function()
        {
            return(firstName + " " + lastName);
        };
        this.SayHello = function()
        {
            alert("Hello, I'm " + firstName + " " + lastName);
        };
    };
    
    var BillGates = new Person("Bill", "Gates", 53);
    var SteveJobs = new Person("Steve", "Jobs", 53);
    
    BillGates.SayHello();
    SteveJobs.SayHello();
    alert(BillGates.getName() + " " + BillGates.age);
    alert(BillGates.firstName);     //这里不能访问到私有变量



    很显然,这种模型的类描述特别象C#语言的描述形式,在一个构造函数里依次定义了私有成员、公共属性和可用的方法,显得非常优雅嘛。特别是“闭包”机制可以模拟对私有成员的保护机制,做得非常漂亮。

    所谓的“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层外层函数体中的临时变量。这使得只要目标对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新的值,和上次那次调用的是各自独立的。的确很巧妙!

    但是前面我们说过,给每一个对象设置一份方法是一种很大的浪费。还有,“闭包”这种间接保持变量值的机制,往往会给JavaSript的垃圾回收器制造难题。特别是遇到对象间复杂的循环引用时,垃圾回收的判断逻辑非常复杂。无独有偶,IE浏览器早期版本确实存在JavaSript垃圾回收方面的内存泄漏问题。再加上“闭包”模型在性能测试方面的表现不佳,微软最终放弃了“闭包”模型,而改用“原型”模型。正所谓“有得必有失”嘛。

    原型模型需要一个构造函数来定义对象的成员,而方法却依附在该构造函数的原型上。大致写法如下:

Js代码 复制代码
  1. //定义构造函数   
  2. function Person(name)   
  3. {   
  4.     this.name = name;   //在构造函数中定义成员   
  5. };   
  6.   
  7. //方法定义到构造函数的prototype上   
  8. Person.prototype.SayHello = function()   
  9. {   
  10.     alert("Hello, I'm " + this.name);   
  11. };       
  12.   
  13. //子类构造函数   
  14. function Employee(name, salary)   
  15. {   
  16.     Person.call(this, name);    //调用上层构造函数   
  17.     this.salary = salary;       //扩展的成员   
  18. };   
  19.   
  20. //子类构造函数首先需要用上层构造函数来建立prototype对象,实现继承的概念   
  21. Employee.prototype = new Person()   //只需要其prototype的方法,此对象的成员没有任何意义!   
  22.   
  23. //子类方法也定义到构造函数之上   
  24. Employee.prototype.ShowMeTheMoney = function()   
  25. {   
  26.     alert(this.name + " $" + this.salary);   
  27. };   
  28.   
  29. var BillGates = new Person("Bill Gates");   
  30. BillGates.SayHello();       
  31.   
  32. var SteveJobs = new Employee("Steve Jobs", 1234);   
  33. SteveJobs.SayHello();   
  34. SteveJobs.ShowMeTheMoney();  
    //定义构造函数
    function Person(name)
    {
        this.name = name;   //在构造函数中定义成员
    };
    
    //方法定义到构造函数的prototype上
    Person.prototype.SayHello = function()
    {
        alert("Hello, I'm " + this.name);
    };    
    
    //子类构造函数
    function Employee(name, salary)
    {
        Person.call(this, name);    //调用上层构造函数
        this.salary = salary;       //扩展的成员
    };
    
    //子类构造函数首先需要用上层构造函数来建立prototype对象,实现继承的概念
    Employee.prototype = new Person()   //只需要其prototype的方法,此对象的成员没有任何意义!
    
    //子类方法也定义到构造函数之上
    Employee.prototype.ShowMeTheMoney = function()
    {
        alert(this.name + " $" + this.salary);
    };
    
    var BillGates = new Person("Bill Gates");
    BillGates.SayHello();    
    
    var SteveJobs = new Employee("Steve Jobs", 1234);
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();



    原型类模型虽然不能模拟真正的私有变量,而且也要分两部分来定义类,显得不怎么“优雅”。不过,对象间的方法是共享的,不会遇到垃圾回收问题,而且性能优于“闭包”模型。正所谓“有失必有得”嘛。

    在原型模型中,为了实现类继承,必须首先将子类构造函数的prototype设置为一个父类的对象实例。创建这个父类对象实例的目的就是为了构成原型链,以起到共享上层原型方法作用。但创建这个实例对象时,上层构造函数也会给它设置对象成员,这些对象成员对于继承来说是没有意义的。虽然,我们也没有给构造函数传递参数,但确实创建了若干没有用的成员,尽管其值是undefined,这也是一种浪费啊。

    唉!世界上没有完美的事情啊!

原型真谛

    正当我们感概万分时,天空中一道红光闪过,祥云中出现了观音菩萨。只见她手持玉净瓶,轻拂翠柳枝,洒下几滴甘露,顿时让JavaScript又添新的灵气。

    观音洒下的甘露在JavaScript的世界里凝结成块,成为了一种称为“语法甘露”的东西。这种语法甘露可以让我们编写的代码看起来更象对象语言。

    要想知道这“语法甘露”为何物,就请君侧耳细听。

    在理解这些语法甘露之前,我们需要重新再回顾一下JavaScript构造对象的过程。

    我们已经知道,用 var anObject = new aFunction() 形式创建对象的过程实际上可以分为三步:第一步是建立一个新对象;第二步将该对象内置的原型对象设置为构造函数prototype引用的那个原型对象;第三步就是将该对象作为this参数调用构造函数,完成成员设置等初始化工作。对象建立之后,对象上的任何访问和操作都只与对象自身及其原型链上的那串对象有关,与构造函数再扯不上关系了。换句话说,构造函数只是在创建对象时起到介绍原型对象和初始化对象两个作用。

    那么,我们能否自己定义一个对象来当作原型,并在这个原型上描述类,然后将这个原型设置给新创建的对象,将其当作对象的类呢?我们又能否将这个原型中的一个方法当作构造函数,去初始化新建的对象呢?例如,我们定义这样一个原型对象:

Js代码 复制代码
  1. var Person =  //定义一个对象来作为原型类   
  2. {   
  3.     Create: function(name, age)  //这个当构造函数   
  4.     {   
  5.         this.name = name;   
  6.         this.age = age;   
  7.     },   
  8.     SayHello: function()  //定义方法   
  9.     {   
  10.         alert("Hello, I'm " + this.name);   
  11.     },   
  12.     HowOld: function()  //定义方法   
  13.     {   
  14.         alert(this.name + " is " + this.age + " years old.");   
  15.     }   
  16. };  
    var Person =  //定义一个对象来作为原型类
    {
        Create: function(name, age)  //这个当构造函数
        {
            this.name = name;
            this.age = age;
        },
        SayHello: function()  //定义方法
        {
            alert("Hello, I'm " + this.name);
        },
        HowOld: function()  //定义方法
        {
            alert(this.name + " is " + this.age + " years old.");
        }
    };

    这个JSON形式的写法多么象一个C#的类啊!既有构造函数,又有各种方法。如果可以用某种形式来创建对象,并将对象的内置的原型设置为上面这个“类”对象,不就相当于创建该类的对象了吗?

    但遗憾的是,我们几乎不能访问到对象内置的原型属性!尽管有些浏览器可以访问到对象的内置原型,但这样做的话就只能限定了用户必须使用那种浏览器。这也几乎不可行。

    那么,我们可不可以通过一个函数对象来做媒介,利用该函数对象的prototype属性来中转这个原型,并用new操作符传递给新建的对象呢?

    其实,象这样的代码就可以实现这一目标:

Js代码 复制代码
  1. function anyfunc(){};           //定义一个函数躯壳   
  2. anyfunc.prototype = Person;     //将原型对象放到中转站prototype   
  3. var BillGates = new anyfunc();  //新建对象的内置原型将是我们期望的原型对象  
    function anyfunc(){};           //定义一个函数躯壳
    anyfunc.prototype = Person;     //将原型对象放到中转站prototype
    var BillGates = new anyfunc();  //新建对象的内置原型将是我们期望的原型对象



    不过,这个anyfunc函数只是一个躯壳,在使用过这个躯壳之后它就成了多余的东西了,而且这和直接使用构造函数来创建对象也没啥不同,有点不爽。

    可是,如果我们将这些代码写成一个通用函数,而那个函数躯壳也就成了函数内的函数,这个内部函数不就可以在外层函数退出作用域后自动消亡吗?而且,我们可以将原型对象作为通用函数的参数,让通用函数返回创建的对象。我们需要的就是下面这个形式:

Js代码 复制代码
  1. function New(aClass, aParams)    //通用创建函数   
  2. {   
  3.     function new_()     //定义临时的中转函数壳   
  4.     {   
  5.         aClass.Create.apply(this, aParams);   //调用原型中定义的的构造函数,中转构造逻辑及构造参数   
  6.     };   
  7.     new_.prototype = aClass;    //准备中转原型对象   
  8.     return new new_();          //返回建立最终建立的对象   
  9. };   
  10.   
  11. var Person =        //定义的类   
  12. {   
  13.     Create: function(name, age)   
  14.     {   
  15.         this.name = name;   
  16.         this.age = age;   
  17.     },   
  18.     SayHello: function()   
  19.     {   
  20.         alert("Hello, I'm " + this.name);   
  21.     },   
  22.     HowOld: function()   
  23.     {   
  24.         alert(this.name + " is " + this.age + " years old.");   
  25.     }   
  26. };   
  27.   
  28. var BillGates = New(Person, ["Bill Gates", 53]);  //调用通用函数创建对象,并以数组形式传递构造参数   
  29. BillGates.SayHello();   
  30. BillGates.HowOld();   
  31.   
  32. alert(BillGates.constructor == Object);     //输出:true  
    function New(aClass, aParams)    //通用创建函数
    {
        function new_()     //定义临时的中转函数壳
        {
            aClass.Create.apply(this, aParams);   //调用原型中定义的的构造函数,中转构造逻辑及构造参数
        };
        new_.prototype = aClass;    //准备中转原型对象
        return new new_();          //返回建立最终建立的对象
    };
    
    var Person =        //定义的类
    {
        Create: function(name, age)
        {
            this.name = name;
            this.age = age;
        },
        SayHello: function()
        {
            alert("Hello, I'm " + this.name);
        },
        HowOld: function()
        {
            alert(this.name + " is " + this.age + " years old.");
        }
    };
    
    var BillGates = New(Person, ["Bill Gates", 53]);  //调用通用函数创建对象,并以数组形式传递构造参数
    BillGates.SayHello();
    BillGates.HowOld();

    alert(BillGates.constructor == Object);     //输出:true



    这里的通用函数New()就是一个“语法甘露”!这个语法甘露不但中转了原型对象,还中转了构造函数逻辑及构造参数。

    有趣的是,每次创建完对象退出New函数作用域时,临时的new_函数对象会被自动释放。由于new_的prototype属性被设置为新的原型对象,其原来的原型对象和new_之间就已解开了引用链,临时函数及其原来的原型对象都会被正确回收了。上面代码的最后一句证明,新创建的对象的 constructor属性返回的是Object函数。其实新建的对象自己及其原型里没有constructor属性,那返回的只是最顶层原型对象的构造函数,即Object。

    有了New这个语法甘露,类的定义就很像C#那些静态对象语言的形式了,这样的代码显得多么文静而优雅啊!

    当然,这个代码仅仅展示了“语法甘露”的概念。我们还需要多一些的语法甘露,才能实现用简洁而优雅的代码书写类层次及其继承关系。好了,我们再来看一个更丰富的示例吧:
    //语法甘露:

Js代码 复制代码
  1. var object =    //定义小写的object基本类,用于实现最基础的方法等   
  2. {   
  3.     isA: function(aType)   //一个判断类与类之间以及对象与类之间关系的基础方法   
  4.     {   
  5.         var self = this;   
  6.         while(self)   
  7.         {   
  8.             if (self == aType)   
  9.               return true;   
  10.             self = self.Type;   
  11.         };   
  12.         return false;   
  13.     }   
  14. };   
  15.   
  16. function Class(aBaseClass, aClassDefine)    //创建类的函数,用于声明类及继承关系   
  17. {   
  18.     function class_()   //创建类的临时函数壳   
  19.     {   
  20.         this.Type = aBaseClass;    //我们给每一个类约定一个Type属性,引用其继承的类   
  21.         for(var member in aClassDefine)   
  22.             this[member] = aClassDefine[member];    //复制类的全部定义到当前创建的类   
  23.     };   
  24.     class_.prototype = aBaseClass;   
  25.     return new class_();   
  26. };   
  27.   
  28. function New(aClass, aParams)   //创建对象的函数,用于任意类的对象创建   
  29. {   
  30.     function new_()     //创建对象的临时函数壳   
  31.     {   
  32.         this.Type = aClass;    //我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类   
  33.         if (aClass.Create)   
  34.           aClass.Create.apply(this, aParams);   //我们约定所有类的构造函数都叫Create,这和DELPHI比较相似   
  35.     };   
  36.     new_.prototype = aClass;   
  37.     return new new_();   
  38. };   
  39.   
  40. //语法甘露的应用效果:       
  41. var Person = Class(object,      //派生至object基本类   
  42. {   
  43.     Create: function(name, age)   
  44.     {   
  45.         this.name = name;   
  46.         this.age = age;   
  47.     },   
  48.     SayHello: function()   
  49.     {   
  50.         alert("Hello, I'm " + this.name + ", " + this.age + " years old.");   
  51.     }   
  52. });   
  53.   
  54. var Employee = Class(Person,    //派生至Person类,是不是和一般对象语言很相似?   
  55. {   
  56.     Create: function(name, age, salary)   
  57.     {   
  58.         Person.Create.call(this, name, age);  //调用基类的构造函数   
  59.         this.salary = salary;   
  60.     },   
  61.     ShowMeTheMoney: function()   
  62.     {   
  63.         alert(this.name + " $" + this.salary);   
  64.     }   
  65. });   
  66.   
  67. var BillGates = New(Person, ["Bill Gates", 53]);   
  68. var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]);   
  69. BillGates.SayHello();   
  70. SteveJobs.SayHello();   
  71. SteveJobs.ShowMeTheMoney();   
  72.   
  73. var LittleBill = New(BillGates.Type, ["Little Bill", 6]);   //根据BillGate的类型创建LittleBill   
  74. LittleBill.SayHello();   
  75.   
  76. alert(BillGates.isA(Person));       //true   
  77. alert(BillGates.isA(Employee));     //false   
  78. alert(SteveJobs.isA(Person));       //true   
  79. alert(Person.isA(Employee));        //false   
  80. alert(Employee.isA(Person));        //true  
    var object =    //定义小写的object基本类,用于实现最基础的方法等
    {
        isA: function(aType)   //一个判断类与类之间以及对象与类之间关系的基础方法
        {
            var self = this;
            while(self)
            {
                if (self == aType)
                  return true;
                self = self.Type;
            };
            return false;
        }
    };
    
    function Class(aBaseClass, aClassDefine)    //创建类的函数,用于声明类及继承关系
    {
        function class_()   //创建类的临时函数壳
        {
            this.Type = aBaseClass;    //我们给每一个类约定一个Type属性,引用其继承的类
            for(var member in aClassDefine)
                this[member] = aClassDefine[member];    //复制类的全部定义到当前创建的类
        };
        class_.prototype = aBaseClass;
        return new class_();
    };
    
    function New(aClass, aParams)   //创建对象的函数,用于任意类的对象创建
    {
        function new_()     //创建对象的临时函数壳
        {
            this.Type = aClass;    //我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类
            if (aClass.Create)
              aClass.Create.apply(this, aParams);   //我们约定所有类的构造函数都叫Create,这和DELPHI比较相似
        };
        new_.prototype = aClass;
        return new new_();
    };

    //语法甘露的应用效果:    
    var Person = Class(object,      //派生至object基本类
    {
        Create: function(name, age)
        {
            this.name = name;
            this.age = age;
        },
        SayHello: function()
        {
            alert("Hello, I'm " + this.name + ", " + this.age + " years old.");
        }
    });
    
    var Employee = Class(Person,    //派生至Person类,是不是和一般对象语言很相似?
    {
        Create: function(name, age, salary)
        {
            Person.Create.call(this, name, age);  //调用基类的构造函数
            this.salary = salary;
        },
        ShowMeTheMoney: function()
        {
            alert(this.name + " $" + this.salary);
        }
    });

    var BillGates = New(Person, ["Bill Gates", 53]);
    var SteveJobs = New(Employee, ["Steve Jobs", 53, 1234]);
    BillGates.SayHello();
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();
    
    var LittleBill = New(BillGates.Type, ["Little Bill", 6]);   //根据BillGate的类型创建LittleBill
    LittleBill.SayHello();
    
    alert(BillGates.isA(Person));       //true
    alert(BillGates.isA(Employee));     //false
    alert(SteveJobs.isA(Person));       //true
    alert(Person.isA(Employee));        //false
    alert(Employee.isA(Person));        //true


    “语法甘露”不用太多,只要那么一点点,就能改观整个代码的易读性和流畅性,从而让代码显得更优雅。有了这些语法甘露,JavaScript就很像一般对象语言了,写起代码了感觉也就爽多了!

    令人高兴的是,受这些甘露滋养的JavaScript程序效率会更高。因为其原型对象里既没有了毫无用处的那些对象级的成员,而且还不存在 constructor属性体,少了与构造函数间的牵连,但依旧保持了方法的共享性。这让JavaScript在追溯原型链和搜索属性及方法时,少费许多工夫啊。

    我们就把这种形式称为“甘露模型”吧!其实,这种“甘露模型”的原型用法才是符合prototype概念的本意,才是的JavaScript原型的真谛!

    想必微软那些设计AJAX架构的工程师看到这个甘露模型时,肯定后悔没有早点把AJAX部门从美国搬到咱中国的观音庙来,错过了观音菩萨的点化。当然,我们也只能是在代码的示例中,把Bill Gates当作对象玩玩,真要让他放弃上帝转而皈依我佛肯定是不容易的,机缘未到啊!如果哪天你在微软新出的AJAX类库中看到这种甘露模型,那才是真正的缘分!

编程的快乐

    在软件工业迅猛发展的今天,各式各样的编程语言层出不穷,新语言的诞生,旧语言的演化,似乎已经让我们眼花缭乱。为了适应面向对象编程的潮流,JavaScript语言也在向完全面向对象的方向发展,新的JavaScript标准已经从语义上扩展了许多面向对象的新元素。与此相反的是,许多静态的对象语言也在向JavaScript的那种简洁而幽雅的方向发展。例如,新版本的C#语言就吸收了JSON那样的简洁表示法,以及一些其他形式的 JavaScript特性。

    我们应该看到,随着RIA(强互联应用)的发展和普及,AJAX技术也将逐渐淡出江湖,JavaScript也将最终消失或演化成其他形式的语言。但不管编程语言如何发展和演化,编程世界永远都会在“数据”与“代码”这千丝万缕的纠缠中保持着无限的生机。只要我们能看透这一点,我们就能很容易地学习和理解软件世界的各种新事物。不管是已熟悉的过程式编程,还是正在发展的函数式编程,以及未来量子纠缠态的大规模并行式编程,我们都有足够的法力来化解一切复杂的难题。

    佛最后淡淡地说:只要我们放下那些表面的“类”,放下那些对象的“自我”,就能达到一种“对象本无根,类型亦无形”的境界,从而将自我融入到整个宇宙的生命轮循环中。我们将没有自我,也没有自私的欲望,你就是我,我就是你,你中有我,我中有你。这时,我们再看这生机勃勃的编程世界时,我们的内心将自然生起无限的慈爱之心,这种慈爱之心不是虚伪而是真诚的。关爱他人就是关爱自己,就是关爱这世界中的一切。那么,我们的心是永远快乐的,我们的程序是永远快乐的,我们的类是永远快乐的,我们的对象也是永远快乐的。这就是编程的极乐!

    说到这里,在座的比丘都犹如醍醐灌顶,心中豁然开朗。看看左边这位早已喜不自禁,再看看右边那位也是心花怒放。

    蓦然回首时,唯见君拈花微笑...

原著:李战(leadzen).深圳 2008-2-23

分享到:
评论

相关推荐

    悟透JavaScript.chm

    悟透JavaScript 在软件工业迅猛发展的今天,各式各样的编程语言层出不穷,新语言的诞生,旧语言的演化,似乎已经让我们眼花缭乱。为了适应面向对象编程的潮流,JavaScript语言也在向完全面向对象的方向发展,新的...

    悟透JavaScript.rar

    "悟透JavaScript"这个压缩包文件显然是一份旨在帮助学习者深入理解这门语言的教程资料。从标签"书籍教程-网页制作"我们可以推测,这份教程可能包含了JavaScript在网页制作中的实际应用和相关理论知识。 "悟透...

    悟透JAVASCRIPT 美绘本

    悟透 JAVASCRIPT 美绘本 插图版

    悟透JavaScript(js)

    ### 悟透JavaScript(js):回归数据与代码的本质 #### 一、引言 《悟透JavaScript》这本书由李战(leadzen)撰写,旨在深入浅出地讲解JavaScript的核心概念和技术要点。本书通过生动有趣的比喻,将抽象的编程概念...

    悟透JavaScript

    资源名称:悟透Javascript内容简介:翻开此书的你,也许是Javascript的崇拜者,正想摩拳擦掌地想尝试下学一学这一精巧的语言;也许是80后,90后的程序员或者前端架构师,正被Javascript魔幻般的...

    悟透javascript(精简版)

    ### 悟透JavaScript核心知识点解析 #### 一、编程世界的本质:数据与代码 在《悟透JavaScript(精简版)》这本书中,作者李战(leadzen)以独特的视角探讨了编程世界的本质——数据与代码之间的关系。他通过生动的...

    轻轻松松学用javascript编程 、悟透JavaScript

    "轻轻松松学用javascript编程" 和 "悟透JavaScript" 这两个主题,旨在帮助初学者和进阶者深入理解和掌握这门语言。 JavaScript的核心概念包括变量、数据类型、操作符、流程控制、函数和对象。变量是存储数据的容器...

    悟透JavaScript.pdf

    ### 悟透JavaScript核心知识点解析 #### 一、编程世界的本质:数据与代码 **悟透JavaScript**这本书深入探讨了编程世界的核心——数据与代码之间的关系。在编程的世界里,一切皆可归结为这两种基本元素:数据与...

    一个月悟透JavaScript

    "一个月悟透JavaScript"这本书显然旨在帮助读者在短时间内深入理解和掌握这门语言的精髓。JavaScript以其灵活、动态的特性,使得网页交互变得更加丰富和生动。下面,我们将根据书名和描述,探讨JavaScript的一些关键...

    悟透JavaScript(PDF),脚本(超好教材)

    ### 悟透JavaScript——回归数据与代码的本质 #### 数据与代码:编程世界的两大基石 在探讨JavaScript之前,我们先从编程世界的基本构成入手。在编程的世界里,无论是哪种编程语言,都离不开两个核心要素:**数据*...

    悟透javascript.pdf

    由于提供的内容中没有实际的文本信息,只是一些重复的网址链接,所以无法从中生成有关JavaScript的具体知识点。但我可以向你介绍一些JavaScript的基础知识点以及一些高级概念,希望对你的学习有所帮助。 JavaScript...

    悟透JavaScript.mht

    悟透JavaScript.mht,悟透JavaScript.mht,悟透JavaScript.mht,悟透JavaScript.mht,悟透JavaScript.mht

    悟透javascript

    《悟透JavaScript:回归数据与代码的本原》 在《悟透JavaScript》一书中,作者李战(leadzen)引领读者深入理解JavaScript的核心概念,强调了编程世界中数据与代码的基本关系及其在JavaScript中的独特体现。本书...

    悟透JAVASCRIPT PDF

    翻开此书的你,也许是JavaScript的崇拜者,正想摩拳擦掌地想尝试下学一学这一精巧的语言;也许是80后,90后的程序员或者前端架构师,正被JavaScript魔幻般的魅力所吸引,所困惑,已经徘徊许久……那么本书正是你所...

Global site tag (gtag.js) - Google Analytics