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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《javascript高级程序设计(第二版)》学习原型与继承相关的知识,希望对你有一定的参考价值。

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

function Person(){}

Person.prototype.name="tom";

//这里等于重写了原型对象
//切断了与构造函数之间的联系
Person.prototype={
    name:"mike",
    age:22
};

//得到的不是Person了,而是Object
console.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); //false
console.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=function(){
        return this.subproperty;
    };

    //重写超类型中的方法
    subP.prototype.getsupvalue=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高级程序设计(第二版)》学习原型与继承的主要内容,如果未能解决你的问题,请参考以下文章

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

《JavaScript高级程序设计(第二版)》学习函数表达式

《JavaScript高级程序设计 第二版》理解对象

细读《从问题到程序(第二版)》第一章学习总结

《JavaScript高级程序设计》啥时候出版

JavaScript学习