首页 > 代码库 > 《javascript高级程序设计(第二版)》学习(4)原型与继承

《javascript高级程序设计(第二版)》学习(4)原型与继承

声明:这类属于学习笔记,主要是摘录书中内容,比较少的整理。内容经常是跳跃的,建议您阅读书本,收益更大。

function Person(){}Person.prototype.name="tom";//这里等于重写了原型对象//切断了与构造函数之间的联系Person.prototype={    name:"mike",    age:22};//得到的不是Person了,而是Objectconsole.log(Person.prototype.constructor);console.log(Person.prototype);
function Person(){}//这里如果实例化在重写原型对象之后,是可以的//因为实例对象[[prototype]]指向的是最初的原型对象Person.prototype={    name:"mike",    sayname:function(){        console.log(this.name);    }};var person1=new Person();person1.sayname();//再次重写但是顺序不同Person.prototype={    name:"tom",    sayname:function(){        console.log(this.name+"hello"); //我们期望的结果应该是mike hello    }};person1.sayname(); //person1是在第二次重写前声明的,但是这里得到的是mike,指向了最初的原型对象var person2=new Person();person2.sayname(); //这里我们得到的是 mike hello 对于他来说,重写的这个是他的最初//总结起来就是:重写原型对象切断了现有原型与之前已经存在对象实例之间的联系;他们引用仍然是最初的原型。

原型有一个问题

function Person(){}Person.prototype={    constructor:Person,    name:"mike",    age:22,    job:"doctor",    friend:["Joe","Tom"],    sayName:function(){        console.log(this.name);    }};var person1=new Person();var person2=new Person();person1.friend.push("Van");console.log(person1.friend); //["Joe", "Tom", "Van"]console.log(person2.friend); //["Joe", "Tom", "Van"]console.log(person1.friend===person2.friend); //true

一般我们为某个实例添加某个属性仅仅是想添加在其身上的
并不想其他实例也有这个方法,然后因为Person中没有friend属性
只有原型中有,所以就直接向原型添加了,导致其他的实例也
自动获取了新的属性

 

对此我们会采用构造函数模式和原型模式来自定义类型

//创建自定义类型常用方式是结合构造函数模式和原型函数模式//构造函数用于定义实例属性function Person(name, age, job){    this.name=name;    this.age=age;    this.job=job;    this.friend=["Joe","Tom"];}//原型模式用于定义方法和共享属性Person.prototype={    constructor:Person,    sayName:function(){        alert(this.name);    }};var person1=new Person("mike", 29, "software engineer");var person2=new Person("pom", 26, "doctor");person1.friend.push("Van");console.log(person1.friend); //["Joe", "Tom", "Van"]console.log(person2.friend); //["Joe", "Tom"]console.log(person1.friend===person2.friend); //falseconsole.log(person1.sayName===person2.sayName); //true

与之相似的还有动态原型模式

//动态原型模式//把所有信息封装在构造函数中,通过在构造函数中初始化原型(在需要的情况下)//又保持二者的优点function Person(name, age, job){    this.name=name;    this.age=age;    this.job=job;    this.friend=["Joe","Tom"];    //方法    if(typeof this.sayName != "function"){        //这里不能使用对象字面量来写原型        //会切断现有实例和原型之间的联系        /*            这种就不行的            Person.prototype={                constructor:Person,                sayName:function(){                    alert(this.name);                }            };        */        Person.prototype.sayName=function(){            alert(this.name);        };    }}

寄生构造模式

    function SpecialArray(){        var values=new Array();        values.push.apply(values, arguments);        values.tojoin=function(){            return this.join("|");        };        return values;    }        var colors=new SpecialArray("red","blue","yellow");    console.log(colors instanceof SpecialArray); //false    console.log(colors instanceof Array); //true    //返回相对于构造函数和其原型属性之间没有关系,这与直接在外部构建对象没有什么不同

稳妥构造函数模式

//稳妥对象durable Objects 时没有公共属性,方法也不引用this的对象。    //最适合在一些安全的环境中(环境会禁止使用this和new),或者防止数据被其他应用程序改动。    //其与寄生构造函数类似,但又有所不同 不使用new调用构造函数    function Person(name, age, job){        var o=new Object();        //可以在这里定义私有变量和函数        o.age=age;        //添加方法        o.sayName=function(){            console.log(name);        };        return o;    }    var friend=Person("Tom",22,"doctor");    friend.sayName(); //tom    console.log(friend.age); //22

在原型中药谨慎的定义其方法

    function SuperP(){        this.property=true;    }    SuperP.prototype.getsupvalue=function(){        return this.property;    };    function subP(){        this.subproperty=false;    }    //继承superP    subP.prototype=new SuperP();    //添加新方法    subP.prototype.getsubvalue=http://www.mamicode.com/function(){        return this.subproperty;    };    //重写超类型中的方法    subP.prototype.getsupvalue=http://www.mamicode.com/function(){        return false;    };    var instance2=new SuperP();    var instance=new subP();    console.log(instance2.getsupvalue()); //true    console.log(instance.getsupvalue()); //false    /*    当通过subP的实例调用getsupervalue时,调用的是重写的方法    而通过superP的实例,则还是原来的方法    */

 

继承

1 包含引用类型值得原型属性会被所有实例共享,通过原型来实现继承时,原型实际上会变成另一个类型的实例,于是原来的实例属性也就变为了原型属性了
2 创建子类的时候,不能向超类的构造函数中传递参数,实际上,是无法再不影响所有对象实例的情况下,给超类的构造函数传递参数。
鉴于上面2个问题,很少单独使用原型链

//问题1的例子    function SuperType(){        this.colors=["red","yellow","blue"];    }    function subType(){}    //继承SuperType    subType.prototype=new SuperType();    var instance1=new subType();    instance1.colors.push("black");    console.log(instance1.colors); //["red", "yellow", "blue", "black"]    var instance2=new subType();     console.log(instance2.colors); //["red", "yellow", "blue", "black"]    /*    这个例子中超类定义了一个colors的属性,包含一个数组。超类各个实例度会有格子包含自己数组的colors属性。
当子类通过原型链继承了超类后,子类原型就变为超类的一个实例,也拥有了colors,与专门创建子类原型的一个color
s属性一样。其实例也会共享,即便实例想单独增加其一个属性,其他实例也会得到。
*/

比其相对好的继承方法是使用借用构造函数

在子类构造函数的内部调用超类构造函数,这样又可以传递参数。

function SuperType(){        this.colors=["red","yellow","blue"];    }    function subType(){        //调用超类构造函数        SuperType.call(this);    }    //继承SuperType    subType.prototype=new SuperType();    var instance1=new subType();    instance1.colors.push("black");    console.log(instance1.colors); //["red", "yellow", "blue", "black"]    var instance2=new subType();     console.log(instance2.colors); //["red", "yellow", "blue"]
    function SuperType(name){        this.name=name;    }    function subType(){        //调用超类构造函数,并传递参数        SuperType.call(this, "mike");        this.age=29;    }    var instance=new subType();    console.log(instance.name);    console.log(instance.age);    /*    仅仅是使用借用构造函数,同样会有构造函数的问题,方法都在构造函数中定义,因此函数复用无从谈起,
而且在超类原型中定义的方法,对于子类来说是不可见的,结果所有类型只能用构造函数模式,因此 借用构造函数也很少单独使用。
*/

最普遍的做法是使用 组合继承

使用原型链和借用构造函数结合。使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实力属性的继承。

function SuperType(name){        this.name=name;        this.colors=["red","yellow","blue"];    }    SuperType.prototype.sayName=function(){        console.log(this.name);    };    function subType(name, age){        //调用超类构造函数        SuperType.call(this, name);        this.age=age;    }    subType.prototype=new SuperType();    subType.prototype.sayAge=function(){        console.log(this.age);    };    var instance1=new subType("mike",22);    instance1.colors.push("black");    console.log(instance1.colors);    instance1.sayAge();    instance1.sayName();    var instance2=new subType("gery",24);    console.log(instance2.colors);    instance2.sayAge();    instance2.sayName();    /*    这样的组合继承避免了原型链和借用构造函数的缺点,融合了他们的各自优点,为了JavaScript常用的继承模式,
instanceof和isPrototypeOf也能够识别基于组合继承创建的对象.
*/

原生式的继承(浅复制)

var person={    name:"mike",    friends:["kobe","lebron","Tim"]};function obj(o){    function F(){}    F.prototype=o;    return new F();}//根据具体需求做修改var anotherperson=obj(person);anotherperson.name="jack";anotherperson.friends.push("rose");var yetanotherperson=obj(person);yetanotherperson.name="lin";yetanotherperson.friends.push("west");console.log(person.name);/*Object.create()方法规范了原型继承。第一个参数:一个用作新对象原型第二个参数:为新对象增加额外属性的对象(与原属性名相同会覆盖)在没有必要兴师动众创建构造函数,而指向让一个对象与另一个对象相似的情况下,原型式继承时可以胜任的,不过,与使用原型模式一样,包含引用类型值得属性都会共享*/

寄生组合式的继承

这种继承主要是客服 组合继承需要两次调用超类的问题

function SuperType(name){    this.name=name;    this.colors=["red","blue","green"];}SuperType.prototype.sayName=function(){    alert(this.name);};function subType(name, age){    SuperType.call(this, name);    this.age=age;}function obj(o){    function F(){}    F.prototype=o;    return new F();}function inheritPrototype(subType, SuperType){    var prototype=obj(SuperType.prototype);    prototype.constructor=subType;    subType.prototype=prototype;}inheritPrototype(subType, SuperType);subType.prototype.sayAge=function(){    alert(this.age);};

YUI的Yahoo.lang.extend()就是采用寄生组合继承

《javascript高级程序设计(第二版)》学习(4)原型与继承