原型对象【深入JavaScript】3.JavaScript继承的实现总结

题记:写这篇博客要主是加深自己对原型对象的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢。

    

一、JavaScript对象

     ECMA-262把对象定义为:“无序属性的集合,其属性可以包括基本值、对象或者函数。”

     说白了就是一组无序名值对,其中值可以是数据或函数。

     

    

二、创立对象的方法总结

    

1.创立Object的实例

    (1)方式:创立一个Object类型的实例,然后再为它添加属性和方法。

    (2)例子:

var person = new Object();
person.name = "kwan";
person.age = 22;
person.job = "student";

person.sayName = function () {
    alert(this.name);
};
(3)优点:简单,轻易理解。

    (4)缺点:应用同一个接口创立很多对象,会产生大量重复代码,且没法识别对象类型。

    (5)总结:这是创立自定义对象的最简单的方式。

 

    

2.工厂模式

    (1)方式:在JS中定义一个工厂函数来实现这一模式。

    (2)例子:

function createPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name);
    };
    return o;
}
 

    (3)优点:增加重复应用的代码。

    (4)缺点:没法识别对象的类型。

    (5)总结:工厂模式抽象了创立具体对象的细节,工厂函数就像一个真正的工厂,只需为它传递参数,就可以流水线式产生响应的对象。

 

    

3.构造函数模式

    (1)方式:创立自定义的构造函数,从而定义自定义对象类型的属性和方法。

    (2)例子:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        alert(this.name);
    };
}
(3)优点:可将它的实例标识为一种特定的类型。

    (4)缺点:每个方法都要在每个实例上从新创立一遍,但其实方法是可以让全部实例同享的。

     而如果简单地将函数定义转移到外部,让对象引用它,那么该模式又失去封装性。

    (5)总结:

     1.构造函数模式中:

     a.没有显式创立对象

     b.直接将属性和方法赋给了this对象

        c.也没有return语句

     d.且函数名首字母大写(惯例)

     2.应用new操纵符调用构造函数创立新实例的机制是:

     a.创立一个新对象

     b.将构造函数的作用域赋给新对象(因此this指向这个新对象)

     c.执行构造函数中的代码

     d.返回新对象

     3.新实例具有一个constructor属性,该属性指向构造函数。

     4.构造函数与其他函数的唯一区分,就在于调用它们的方式不同。调用构造函数时需用new操纵符。任何函数通过new操纵符调用都市酿成构造函数,而原本的构造函数若不应用new操纵符而是直接调用,那和普通的函数没什么两样,此时的this指代调用该函数的作用域。

    

    

4.原型模式

    (1)方式:通过函数的prototype属性,将可以同享的信息直接添加到原型对象中。

    (2)例子:

function Person() {}

Person.prototype.name = "kwan";
Person.prototype.age = "22";
Person.prototype.job = "student";
Person.prototype.sayName = function () {
    alert(this.name);
};
(3)优点:可以让全部对象实例同享其原型对象所包括的属性和方法。

    (4)缺点:首先,它省略了为构造函数传递初始化参数这一环节,结果全部实例在默认情况下都将获得相同的属性值;

       其次,由于其同享的本性,当属性值为引用类型时,会致使很大问题。

    (5)总结:

     1.我们创立的每个函数都有一个prototype属性,该属性值为指针,指向一个对象,即原型对象,它的用途是包括可以由特定类型的全部实例同享的属性和方法。

     2.应用原型模式创立的对象都在应用同一组属性和函数,这便是该模式的同享特性。

     3.若实例属性与原型对象属性同名,则优先取实例属性值(屏蔽)

     4.实例的外部属性__proto__指向原型对象,这便是两者之间的连接。继承的实现,以及对象属性的查找,其实都是依附于这个连接。

        5.读取对象属性的过程是:先找实例,后找原型。

     6.原型是动态的,因此我们对原型对象所做的任何修改都能够立即从实例上反应出来。

 

    

5.组合应用构造函数模式和原型模式(优化计划)

    (1)方式:构造函数模式用于定义实例属性,而原型模式用于定义方法和同享属性。

    (2)例子:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["kwan", "soyi"];
}

Person.prototype = {
    constructor : Person,
    sayName : function () {
        alert(this.name);
    }
}
(3)优点:每个实例都市有自己的一份实例属性的副本,同时又同享对方法和同享属性的引用,最大限度地节省了内存;另外,该模式支撑向构造函数传递参数。

    (4)缺点:封装性缺乏。

    (5)总结:挺好用。

 

    

6.动态原型模式

    (1)方式:把全部信息都封装在了构造函数中,通过检查某个应该存在的方法是不是有效,来决议是不是需要初始化原型。

    (2)例子:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["kwan", "soyi"];
    
    if (typeof this.sayName != "function") {//这个判断是须要的,且只需写一个条件
        Person.prototype.sayName = function () {
            alert(this.name);
        };
    }
}
(3)优点:仅在须要情况下通过在构造函数中初始化原型,坚持同时应用构造函数和原型的优点,封装度更高。

    (4)缺点:暂无。

    (5)总结:

     通过if条件的判断,只有在第一个实例被new出来的时候才会为原型对象添加属性和方法,接下来的全部对象的实例化均不会调用添加操纵,而且这样写可将全部信息封装在构造函数中。

 

    

7.寄生构造函数模式(了解)

    (1)方式:创立一个函数,该函数的作用仅仅是封装创立对象的代码,然后再返回新创立的对象。

    (2)例子:

function Person(name, age, job) {
    var o = new Ojbect();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name);
    };
    return o;
}
(3)优点:暂无。

    (4)缺点:返回的对象与构造函数或构造函数的原型之间没有关系,为此不能依附instanceof操纵符来确定对象类型。

    (5)总结:

     1.构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数的末尾添加一个return语句,可以重写返回值。

     2.这个模式的应用条件是:在特殊情况下用来为对象创立构造函数。尽量不要用这个模式。

 

    

8.稳当构造函数模式(了解)

    (1)方式:稳当构造函数遵守与寄生构造函数相似的模式,但区分是:新创立对象的实例方法不引用this,并且不应用new操纵符调用构造函数。

    (2)例子:

function Person(name, age, job) {
    var o = new Ojbect();

    o.sayName = function () {
        alert(name);
    };
    
    return o;
}
(3)优点:暂无。

    (4)缺点:返回的对象与构造函数或构造函数的原型之间没有关系,为此不能依附instanceof操纵符来确定对象类型。

    (5)总结:

     1.所谓稳当对象,指的是没有大众属性,而且其方法也不引用this的对象。

     2.稳当对象最适合在一些安全的环境中(这些环境中会制止应用this和new),或者在防止数据被其他应用程序(如Mashup)改动时应用。

     

    

三、继承总结

     在ECMAScript中没法实现接口继承,它只支撑实现继承,而且其实现继承主要是依托原型链来实现的。

    

1.原型链继承

    (1)什么是原型链?

     原型链的基本思想是:利用原型让一个引用类型继承另一个引用类型的属性和方法。

     我们让原型对象A即是另一个类型的实例,此时的原型A将包括一个指向另一个原型B的指针,响应地,原型B中也包括着一个指向另一个构造函数的指针。假如原型B又是另一个原型C的实例,那么上述关系仍然建立,如此层层推进,就构成了实例与原型的链条(A->B->C)。这就是原型链的观点。

    每日一道理
巴尔扎克说过“不幸,是天才的进升阶梯,信徒的洗礼之水,弱者的无底深渊”。风雨过后,眼前会是鸥翔鱼游的天水一色;走出荆棘,前面就是铺满鲜花的康庄大道;登上山顶,脚下便是积翠如云的空蒙山色。 在这个世界上,一星陨落,黯淡不了星空灿烂,一花凋零,荒芜不了整个春天。人生要尽全力度过每一关,不管遇到什么困难不可轻言放弃。

    (2)例子:

function SuperType(){ //父类
    this.property=true;
}

//在父类原型中添加方法
SuperType.prototype.getSuperValue=function(){
    return this.property;
};

function SubType(){  //子类
    this.subproperty=false;
}

//实现继承的代码:
SubType.prototype=new SuperType();

//在子类的新原型中添加方法
SubType.prototype.getSubValue=function(){
    return this.subproperty;
};
(3)原型链的本质是什么?

     利用原型链实现继承的本质是重写原型对象,代之以一个新类型的实例。

     同时,通过实现原型链,本质上扩展了原型搜索机制。

     在通过原型链实现继承后,搜索属性或方法的过程就得以沿着原型链继承向上。

    (4)或许来一个图,会更加清晰:

    原型和对象

    (5)原型链的问题:

     1.当存在包括引用类型值的原型时,某个实例对引用类型值的修改,会反应在其余全部实例上;

     2.在创立子类型的实例时,没有办法在不影响全部对象实例的情况下,给超类型的构造函数传递参数。

 

    

2.借用构造函数(伪造对象、经典继承)

    (1)基本思想:在子类型构造函数的外部调用超类型构造函数。

    (2)例子:

function SuperType() { //父类
    this.colors = ["red", "blue", "green"];
}

function SubType() {
    //继承SuperType
    SuperType.call(this);
}
(3)优点:可在子类型构造函数中向超类型构造函数传递参数。

    (4)缺点:仅应用此技巧实现继承,函数没法复用。而且在超类型的原型中定义的方法,对子类型而言也是不可见的。

    (5)总结:

     仅仅借用构造函数实现继承,本质上是没有利用到原型链的。因此很少单独应用该模式。

 

    

3.组合继承(伪经典继承)

    (1)方式:将原型链和借用构造函数的技巧组合在一块。

       其基本思想是:应用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。

    (2)例子:

function SuperType(name) { //父类
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function () {
    alert(this.name);
};

function SubType(name, age) {
    //继承SuperType的实例属性
    SuperType.call(this, name); //第二次调用超类型构造函数
    
    this.age = age;
}

//继承方法
SubType.prototype = new SuperType(); //第一次调用超类型构造函数

SubType.prototype.sayAge = function () {
    alert(this.age);
};
(3)优点:既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

    (4)缺点:无论在什么情况下,都市调用两次超类型构造函数,以至于重写超类型对象的实例属性,形成空间的浪费。(这个自己画图或看书理解)

    (5)总结:最经常使用的继承模式。

 

    

4.寄生组合式继承

    (1)方式:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

    (2)基本思绪:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的不过就是超类型原型的一个副本而已。本质上,就是应用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

    (3)例子:

     首先,寄生组合式继承的基本模式是:

function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype); //创立对象
    prototype.constructor = subType; //增强对象
    subType.prototype = prototype; //指定对象
}

//原型式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
示例中,object函数首先生成一个空白构造函数F,再将传入的对象o指定为F的原型对象,最后返回一个F的实例。

     这样一来,返回的对象的原型就是o,而本身这个对象是空白的。

 

     而inheritPrototype函数的作用是:

     首先调用object函数,传入超类型的原型,从而得到一个F的空白实例,此时该实例的原型就是超类型的原型;

     然后,将这个实例的构造函数指针指定为subType,因此这个实例是用作subType的原型的;

     最后,将第一步所得的空白实例对象指定为subType的原型。

     至此,subType的原型不再是superType的实例,而是一个空白的实例,但这个实例原型又是suerType。

     上面给出示例2:

function SuperType(name) { //父类
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function () {
    alert(this.name);
};

function SubType(name, age) {
    //继承SuperType的实例属性
    SuperType.call(this, name); //仅这一次调用超类型构造函数
    
    this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function () {
    alert(this.age);
};

//继承方法
SubType.prototype = new SuperType(); //第一次调用超类型构造函数

SubType.prototype.sayAge = function () {
    alert(this.age);
};
(4)优点:这个例子的高效率体现在它只调用一次SuperType的构造函数,并且因此避免了在SubType.prototype上创立不须要的、过剩的属性。与此同时,原型链还能坚持不变。

 

    

5.原型式继承(了解)

    (1)方式:借助原型可以基于已有的对象创立新对象,同时还不必因此创立自定义类型。

    (2)例子:

//原型式继承
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}
(3)本质:object()对传入其中的对象执行了一次浅复制。

    (4)应用条件:这类继承模式要求必须有一个对象可以作为另一个对象的基础。如果有这么一个对象的话,可以把它传递给object()函数,然后再根据具体需求对得到的对象加以修改便可。

 

    

6.寄生式继承(了解)

    (1)思绪:与寄生构造函数和工厂模式相似,即创立一个仅用于封装继承过程的函数,该函数在外部以某种方式来增强对象,最后再像是真的是它做了全部工作一样返回对象。

    (2)例子:

function createAnother(original) {
    var clone = object(original); //通过调用函数创立一个新对象
    clone.sayHi = function () {
        alert("hi");
    };
    //以某种方式来增强这个对象
    return clone;
}
 

    

四、总结

    这一篇总结写到我吐血...估计看的人也吐血了吧...已经尽可能简单化了,希望各位爱好。

    共勉。

 

文章结束给大家分享下程序员的一些笑话语录: Borland说我很有前途,Sun笑了;Sun说我很有钱,IBM笑了;IBM说我很专业,Sybase笑了;Sybase说我数据库很牛,Oracle笑了;Oracle说我是开放的,Linux笑了;Linux说我要打败Unix,微软笑了;微软说我的系统很稳定,我们都笑了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值