原型是js中非常特殊一个对象,当一个函数创建之后,会随之就产生一个原型对象。当通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性指向原型。

原型有四种状态:

//第一种状态
    function Person(){

    }
    //第二种状态
    Person.prototype.name = "Leon";
    Person.prototype.age = 23;
    Person.prototype.say = function() {
        alert(this.name+","+this.age);
    }
    //第三中状态,创建了一个对象之后会有一个_prop_的属性指向原型
    //在使用时如果在对象内部没有找到属性会去原型中找,_prop_属性是隐藏的
    var p1 = new Person();
    p1.say();//Leon,23
    //以下方法可以检测出p1是否有_prop_指向Person的原型
    alert(Person.prototype.isPrototypeOf(p1));//true

    //第四种状态
    var p2 = new Person();
    //是在自己的空间中定义了一个属性,不会替换原型中的属性
    p2.name = "Ada";
    p2.say();//Ada,23
    p1.say();//Leon,23

原型的检测

//检测某个对象是否是某个函数的原型
    alert(Person.prototype.isPrototypeOf(p2));//true

    //检测某个对象的constructor
    alert('p1.constructor==Person? '+(p1.constructor==Person));//true
    alert('p2.constructor==Person? '+(p2.constructor==Person));//true

    //检测某个属性是否是自己的属性
    alert(p1.hasOwnProperty("name"));//false,p1自己的空间中没有值
    alert(p2.hasOwnProperty("name"));//true,p2在自己的空间中设置了name属性

    delete p2.name;
    p2.say();//Leon,23
    alert(p2.hasOwnProperty("name"));//由于已经删除了,所以是false

    //检测某个对象在原型或者自己中是否包含有某个属性,通过in检测
    alert('is name in p1: '+("name" in p1));//true
    alert('is name in p2: '+("name" in p2));//true
    alert('is address in p1: '+("address" in p1));//在原型和自己的空间中都没有,false

    alert(hasPrototypeProperty(p1,"name"));//true
    alert(hasPrototypeProperty(p2,"name"));//false
    /**
     * 可以通过如下方法检测某个属性是否在原型中存在
     */
    function hasPrototypeProperty(obj,prop) {
        return ((!obj.hasOwnProperty(prop))&&(prop in obj))
    }

通过json的格式来编写原型

使用上面的方式来编写原型,当属性和方法特别多时,编写起来不是很方便,可以通过json的格式来编写 。

/**
     * 以下方式将会重写原型
     * 由于原型重写,而且没有通过Person.prototype来指定
     * 此时的constructor不会再指向Person而是指向Object
     * 如果constructor真的比较重要,可以在json中说明原型的指向
     */
    Person.prototype = {
        constructor:Person,//手动指定constructor,如果不指定则p1.constructor==Person返回false
        name:"Leon",
        age:23,
        say:function() {
            alert(this.name+","+this.age);
        }
    }
    var p1 = new Person();
    p1.say();
    alert(p1.constructor==Person);//true

基于原型的创建虽然可以有效的完成封装,但是依然有一些问题
1、无法通过构造函数来设置属性值
2、当属性中有引用类型变量时,可能存在变量值重复

function Person(){

    }
    Person.prototype = {
        constructor:Person,
        name:"Leon",
        age:30,
        friends:["Ada","Chris"],
        say:function() {
            alert(this.name+"["+this.friends+"]");
        }
    }
    var p1 = new Person();
    p1.name = "John";
    p1.say();//john[ada,chris]
    //会在原型中找friends,所以Mike是在原型中增加的
    p1.friends.push("Mike");//为p1增加了一个朋友
    var p2 = new Person();
    //此时原型中就多了一个Mike,这就是原型所带来的第二个问题
    p2.say();//leon ada chris mike

通过组合构造函数和原型来实现对象的创建

为了解决原型所带来的问题,此处需要通过组合构造函数和原型来实现对象的创建。将属性在构造函数中定义,将方法在原型中定义。这种有效集合了两者的优点,是目前最为常用的一种方式。

function Person(name,age,friends){
        //属性在构造函数中定义
        this.name = name;
        this.age = age;
        this.friends = friends;
    }
    Person.prototype = {
        constructor:Person,
        //方法在原型中定义
        say:function() {
            alert(this.name+"["+this.friends+"]");
        }
    }
    //此时所以的属性都是保存在自己的空间中的
    var p1 = new Person("Leon",23,["Ada","Chris"]);
    p1.name = "John";
    p1.friends.push("Mike");//为p1增加了一个朋友
    p1.say();//john [ada chris mike]
    var p2 = new Person("Ada",33,["Leon"]);
    p2.say();// ada [leon]

动态原型的构造方式

为了让定义的方式更加符合java的需求,就把定义方法的原型代码放置到Person这个构造函数中。不能使用重写的方式定义方法。

function Person(name,age,friends){
        //属性在构造函数中定义
        this.name = name;
        this.age = age;
        this.friends = friends;

        //不能使用重写的方式定义
        /*Person.prototype = {
            constructor:Person,
            //方法在原型中定义
            say:function() {
                alert(this.name+"["+this.friends+"]");
            }
        }*/
        /**
         * 判断Person.prototype.say是否存在,如果不存在就表示需要创建
         * 当存在之后就不会在创建了
         */
        if(!Person.prototype.say) {
            Person.prototype.say = function() {
                alert(this.name+"["+this.friends+"]");
            }   
        }
    }

    //此时所以的属性都是保存在自己的空间中的
    var p1 = new Person("Leon",23,["Ada","Chris"]);
    p1.name = "John";
    p1.friends.push("Mike");//为p1增加了一个朋友
    p1.say();//john [ada chris mike]
    var p2 = new Person("Ada",33,["Leon"]);
    //此时原型中就多了一个Mike,这就是原型所带来的第二个问题
    p2.say();// ada [leon]

扩展已经存在类的原型

/**
     * 以下等于为String扩展了一个trim的方法来去除空格
     */
    String.prototype.trim = function() {
        //js的正则表达式和java很类似,区别就是js是使用//
        return this.replace(/(^\s+)|(\s+$)/g,"");
    }
    var str = "   aaaa   ";
    alert("|"+str.trim()+"|");