web前端之悟透JavaScript三:JavaScript真经(原型)

web前端之悟透JavaScript三:JavaScript真经(原型)

1.初看原型:
prototype在软件界翻译成“原型”,代表事物的初始形态,也含有模型和样板的意义。JavaScript的所有function类型的对象都有一个prototype属性。这个prototype属性本身也是一个object类型的对象,因此我们也可以为这个prototype对象添加任意的属性和方法。事实上,在构造函数的prototype上定义的所有属性和方法,都是可以通过其构造的对象直接访问和调用的。也可以这么说,prototype提供了一群同类对象共享属性和方法的机制。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>初看原型</title>
    <script type="text/javascript">
    function Person(name){
        this.name=name;
    };
    Person.prototype.SayHello=function(){//给Person函数的prototype添加SayHello方法
        alert("Hello,I'm "+this.name);
    };
    var BillGates=new Person("Bill Gates");
    var SteveJobs=new Person("Steve Jobs");
    BillGates.SayHello();
    SteveJobs.SayHello();
    alert(BillGates.SayHello==SteveJobs.SayHello);
    </script>
</head>
<body>

</body>
</html>

构造函数的prototype上定义的方法确实可以通过对象直接调用到,而且代码是共享的。显然,把方法设置到prototype的写法显得优雅多了,尽管调用形式没有改变,但是逻辑上却体现了方法与类的关系,更容易理解

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title></title>
    <script type="text/javascript">
    function Person(name){
        this.name=name;
    };
    Person.prototype.SayHello=function(){//给Person函数的prototype添加SayHello方法
        alert("Hello,I'm "+this.name);
    };
    function Employee(name,salay){
        Person.call(this,name);
        this.salay=salay;
    };
    Employee.prototype=new Person();//建一个基类的对象作为子类原型的原型(原型继承)

    Employee.prototype.ShowMeTheMoney=function(){
        alert(this.name+" $ "+this.salay);
    };
    var BillGates=new Person("Bill Gates");
    var SteveJobs=new Employee("Steve Jobs",1234);

    BillGates.SayHello();
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();

    alert(BillGates.SayHello==SteveJobs.SayHello);
    </script>
</head>
<body>

</body>
</html>

Employee.prototype=new Person(),构造了一个基类的对象,并将其设为子类的构造函数的prototype,这个是很有意思的。这样做的目的是为了后面,通过子类对象也可以直接调用基类prototype的方法。
在JavaScript中,prototype不但能让对象共享自己的财富,而且prototype还有寻根问主的天性,从而使得先辈们的遗产可以代代相传。当从一个对象那里读取属性或调用方法时,如果该对象自身不存在这样的属性和方法,就会去自己关联的prototype对象那里寻找;如果prototype没有,又会去prototype自己关联的前辈配prototype哪里寻找,直到找到或直到追溯结果结束。
在JavaScript内部,对象的属性和方法追溯机制使用过所谓的prototype链来实现的。当用new操作符构造对象时,也会同时将构造函数的prototype对象指派给新创建的对象,成为该对象内置的原型对象。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>Document</title>
    <script type="text/javascript">
    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();
    var SteveJobs=new Person("Steve Jobs");
    SteveJobs.company="Apple";
    SteveJobs.SayHello=function(){
        alert("Hi,"+this.name+" like "+this.company+".ha ha");
    };
    SteveJobs.SayHello();
    BillGates.SayHello();
    </script>
</head>
<body>

</body>
</html>

对象可以覆盖原型对象的那些属性和方法,一个构造函数原型对象也可以掩盖上层构造函数原型对象既有的属性和方法。这种掩盖其实只是在对象自己身上创建了新的属性和方法,只不过这些属性和方法与原型对象的那些同名而已。JavaScript就是用这个简单的掩盖机制来实现对象的“多态性”,与静态对象语言的虚函数和重载概念不谋而合。
然而,比静态对象语言更神奇的是,我们可以随时给原型对象动态添加新的属性和方法,从而动态地扩展基类的功能特性,现在让我们看下面这个代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title></title>
    <script type="text/javascript">
    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();
    </script>
</head>
<body>

</body>
</html>

2.原型扩展:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>模拟闭包机制</title>
    <script type="text/javascript">
    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",51);

    BillGates.SayHello();
    SteveJobs.SayHello();
    alert(BillGates.getName()+" "+BillGates.age);
    alert(BillGates._firstName);//这里不能访问私有变量
    </script>
</head>
<body>

</body>
</html>

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

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>Document</title>
    <script type="text/javascript">
    function Person(name){
        this.name=name;
    };
    Person.prototype.SayHello=function(){
        alert("Hello ,I'm "+this.name);
    };
    //子类构造函数
    function Employee(name,salay){
        Person.call(this,name);//调用上层构造函数
        this.salay=salay;
    };
    //在自雷构造函数首先需要用上层构造函数来建立prototype对象,实现继承的概念
    Employee.prototype=new Person();
    Employee.prototype.ShowMeTheMoney=function(){
        alert(this.name+" $ "+this.salay);
    };
    var BillGates=new Person("Bill Gates");
    BillGates.SayHello();
    var SteveJobs=new Employee("Steve Jobs",1234);
    SteveJobs.SayHello();
    SteveJobs.ShowMeTheMoney();
    </script>
</head>
<body>

</body>
</html>

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

编程世界里只存在两种基本元素,一个是数据,一个是代码。编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力。     数据天生就是文静的,总想保持自己固有的本色;而代码却天生活泼,总想改变这个世界。      你看,数据代码间的关系与物质能量间的关系有着惊人的相似。数据也是有惯性的,如果没有代码来施加外力,她总保持自己原来的状态。而代码就象能量,他存在的唯一目的,就是要努力改变数据原来的状态。在代码改变数据的同时,也会因为数据的抗拒而反过来影响或改变代码原有的趋势。甚至在某些情况下,数据可以转变为代码,而代码却又有可能被转变为数据,或许还存在一个类似E=MC2形式的数码转换方程呢。然而,就是在数据和代码间这种即矛盾又统一的运转中,总能体现出计算机世界的规律,这些规律正是我们编写的程序逻辑。     不过,由于不同程序员有着不同的世界观,这些数据和代码看起来也就不尽相同。于是,不同世界观的程序员们运用各自的方法论,推动着编程世界的进化和发展。       众所周知,当今最流行的编程思想莫过于面向对象编程的思想。为什么面向对象的思想能迅速风靡编程世界呢?因为面向对象的思想首次把数据和代码结合成统一体,并以一个简单的对象概念呈现给编程者。这一下子就将原来那些杂乱的算法与子程序,以及纠缠不清的复杂数据结构,划分成清晰而有序的对象结构,从而理清了数据与代码在我们心中那团乱麻般的结。我们又可以有一个更清晰的思维,在另一个思想高度上去探索更加浩瀚的编程世界了。     在五祖弘忍讲授完《对象真经》之后的一天,他对众弟子们说:“经已讲完,想必尔等应该有所感悟,请各自写个偈子来看”。大弟子神秀是被大家公认为悟性最高的师兄,他的偈子写道:“身是对象树,心如类般明。朝朝勤拂拭,莫让惹尘埃!”。此偈一出,立即引起师兄弟们的轰动,大家都说写得太好了。只有火头僧慧能看后,轻轻地叹了口气,又随手在墙上写道:“对象本无根,类型亦无形。本来无一物,何处惹尘埃?”。然后摇了摇头,扬长而去。大家看了慧能的偈子都说:“写的什么乱七八糟的啊,看不懂”。师父弘忍看了神秀的诗偈也点头称赞,再看慧能的诗偈之后默然摇头。就在当天夜里,弘忍却悄悄把慧能叫到自己的禅房,将珍藏多年的软件真经传授于他,然后让他趁着月色连夜逃走...     后来,慧能果然不负师父厚望,在南方开创了禅宗另一个广阔的天空。而慧能当年带走的软件真经中就有一本是《JavaScript真经》!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值