悟透Javascript之 原型prototype

构造函数的Prototype上定义的方法确实可以通过对象直接调用,而且代码是共享的。我表示我不懂。太难理解了,艹。
在Javascript中,prototype不但能让对象共享自己的财富,而且prototype还有寻根问祖的天性。
当从一个对象那里读取属性或调用方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象里寻找。如果prototype没有,
又会去prototype自己关联的前辈prototype那里寻找,直到找到或追溯过程结束为止。

我们先从Javascript中的构造函数说起,

<script type="text/javascript">
    function Person(name){//带参数的构造函数
        this.name = name;//定义并初始化name属性
        this.SayHello = function(){//定义对象方法SayHello
            alert("Hello,I'm " + this.name);
        }
    }

    function Employee(name,salary)//子构造函数
    {
        Person.call(this,name);//调用父构造函数
        this.salary = salary;//添加并初始化salary属性
        this.ShowMeTheMoney = function(){
            //添加对象方法ShowMeTheMoney
            alert(this.name+" $"+this.salary);
        }
    }
    var BillGates = new Person("Bill Gates");//创建Person对象
    var SteveJobs = new Employee("Steve Jobs",9999);//创建Employee类的SteveJobs对象
    BillGates.SayHello();//输出I'm Bill Gates
    SteveJobs.SayHello();//输出I'm Steve Jobs
    SteveJobs.ShowMeTheMoney();//Steve Jobs $9999

    alert(BillGates.constructor == Person);//true
    alert(SteveJobs.constructor == Employee);//true
    alert(BillGates.SayHello == SteveJobs.SayHello);//false
</script>

这里的Employee继承了Person,包括属性设置和方法。但是这里的SayHello方法却不是一个方法,而是在Employee中新开辟的一个方法。相当于复制过来的方法。

而是用prototype就不是这样了。prototype可以让Employee共享Person的原型方法。

如果Person创建两个对象,那么它们调用同一个方法吗?

我们来看代码:

<script type="text/javascript">
    function SayHello(){
        alert("Hello,I'm "+this.name);
    }
    function Person(name){
        this.name = name;
        this.SayHello = SayHello;
    }
    var BillGates = new Person("Bill Gates");
    var SteveJobs = new Person("Steve Jobs");
    BillGates.SayHello();//Hello,I'm Bill Gates
    SteveJobs.SayHello();//Hello,I'm Steve Jobs
    alert(BillGates.SayHello==SteveJobs.SayHello);//true
</script>

结果发现,是的,它们的确调用一个方法。而不是像Employee那么复制出的新方法。

上述代码转化成原型模式,也很简单,如下所示,先设置属性,再通过原型定义方法:

<script type="text/javascript">
    function Person(name){
        this.name = name;//设置对象属性,每个对象各有一份属性数据
    }
    Person.prototype.SayHello = function(){//构造函数的Prototype上定义的方法确实可以通过对象直接调用,而且代码是共享的。我表示我不懂。太难理解了,艹。
        alert("Hello,I'm "+this.name);
    }
    var BillGates = new Person("Bill Gates");
    var SteveJobs = new Person("Steve Jobs");
    BillGates.SayHello();//Hello,I'm Bill Gates
    SteveJobs.SayHello();//Hello,I'm Steve Jobs
    alert(BillGates.SayHello==SteveJobs.SayHello);//true

</script>

"原型继承"是慈祥而又严厉的。原型对象将自己的属性和方法无私地贡献给孩子们使用,也并不强迫孩子们必须遵从,允许一些顽皮孩子按自己的兴趣和爱好独立行事。
我们来进一步研究原型prototype的特点,

<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();//由于继承了原型的东西,规规矩矩输出: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");
    }
    BillGates.SayHello();//没有被改变,还是输出Hello,I'm Bill Gates of Microsoft
    SteveJobs.SayHello();//原有的都被覆盖掉了,输出Hi, SteveJobs like Aplle,ha ha
</script>

这里原型中的属性和方法可以被覆盖掉。但原型自身的并没有受到影响。

对象可以掩盖原型对象的那些属性和方法,一个构造函数原型对象也可以掩盖上层构造函数原型对象既有的属性和方法。
我们可以随时给原型对象动态添加新的属性和方法,从而动态扩展基类的功能特性。我们来看下面的代码:

<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+" ,byebye~!");
    }
    BillGates.Retire();//扩展的方法可以立刻使用
</script>

这里后续动态添加的方法,之前的对象已然可以使用,就是这么神奇。

最后我们用一个稍微复杂的例子来说明原型的使用:

<script type="text/javascript">
    function Person(name){//构造函数
        this.name = name;
    }
    Person.prototype.SayHello = function(){//原型方法
        alert("Hello,I'm "+this.name);
    }
    //子类构造函数
    function Employee(name,salary){
        //Person.call(this,name);//调用上级的构造函数
        this.name = 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();
    //BillGates.ShowMeTheMoney();//不能用,因为Person的prototype里并没有这个方法

    var SteveJobs = new Employee("Steve Jobs",1234);
    SteveJobs.SayHello();//继承了Person的prototype方法
    SteveJobs.ShowMeTheMoney();
    alert(BillGates.SayHello == SteveJobs.SayHello);//True
</script>

这里的两个SayHello是同一个方法哦,也就是调用同一个方法产生的哦,跟之前的Employee复制过来的有区别哦。这点,读着可以细细体会其中的奥秘。

总之,原型prototype可以共享方法,"原型继承"是慈祥而又严厉的。原型对象将自己的属性和方法无私地贡献给孩子们使用,也并不强迫孩子们必须遵从,允许一些顽皮孩子按自己的兴趣和爱好独立行事。

原文地址:https://www.cnblogs.com/jiqing9006/p/3154734.html