javascript面向对象二-“继承”

让我们接着上篇博客“javascript-面向对象,你了解多少”继续javascript的面向对象特征。这次说“继承”。

当开始听到javascript的继承时有点疑惑,因为在javascript中没有“类”的概念,怎么会继承呢?在javascript中有什么呢?有对象,有函数,通常在一个对象中定义变量,属性,还定义函数(内置)。这种结构像极了类。继承其实就是复用一些东西,在javascript中,我们可以把这种函数看成是一个类,然后进行继承。

“继承”在javascript中可以实现继承的方法有两种形式。

一、原型继承法

每一个类(函数)都有一个原型,该原型上的成员在该类实例化时,会传给该类的实例化对象。实例化的对象上没有原型,但是它可以作为另一个类(函数)的原型,当以该对象为原型的类实例化时,该对象上的成员就会传给以它为原型的类的实例化对象上。这就是原型继承的本质。


function parentClass() {
 
    // private field
 
    var x = "I‘m a parentClass field!";
 
    // private method
 
    function method1() {
 
        alert(x);
 
        alert("I‘m a parentClass method!");
 
    }
 
    // public field
 
    this.x = "I‘m a parentClass object field!";
 
    // public method
 
    this.method1 = function() {
 
        alert(x);
 
        alert(this.x);
 
        method1();
 
    }
 
}
 
parentClass.prototype.method = function () {
 
    alert("I‘m a parentClass prototype method!");
 
}
 
parentClass.staticMethod = function () {
 
    alert("I‘m a parentClass static method!");
 
} 
 
function subClass() {
 
    // private field
 
    var x = "I‘m a subClass field!";
 
    // private method
 
    function method2() {
 
        alert(x);
 
        alert("I‘m a subClass method!");
 
    }
 
    // public field
 
    this.x = "I‘m a subClass object field!";
 
    // public method
 
    this.method2 = function() {
 
        alert(x);
 
        alert(this.x);
 
        method2();
 
    }
 
    this.method3 = function() {
 
        method1();
 
    }
 
} 
 
// inherit
subClass.prototype = new parentClass();// new 新对象,赋值给子类.
// 等同于var p=parentClass();subClass.prototype=p;首先创建一个父类的实例化对象然后将该对象赋给子类的 prototype 属性. 
subClass.prototype.constructor = subClass; // 将子类本身赋值给它的 prototype 的 constructor 属性。注意:这里赋值是没有 () 。
 
// test
var o = new subClass();// 实例化子类
alert(o instanceof parentClass);    // true
alert(o instanceof subClass);       // true
// 通过继承,父类中的所有公有实例成员都会被子类继承。子类的实例化对象既属于子类,也属于父类。 
 
alert(o.constructor);  // function subClass() {...} // 通过原型赋值,查看子类的实例化对象的 constructor 属性子类而不是其父类。
 
o.method1();    // I‘m a parentClass field!
 
                // I‘m a subClass object field!
 
                // I‘m a parentClass field!
 
                // I‘m a parentClass method!
//子类继承来的公有实例方法中,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于父类的。
 
o.method2();    // I‘m a subClass field!
 
                // I‘m a subClass object field!
 
                // I‘m a subClass field!
 
                // I‘m a subClass method!
//子类中定义的实例方法,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于子类的。
 
o.method();     // I‘m a parentClass prototype method! 定义在父类原型上的方法,会被子类继承。 
 
o.method3();               // Error!!!子类中定义的实例方法是不能访问父类中定义的私有实例成员的。
 
subClass.staticMethod();   // Error!!!静态成员是不会被继承的

二、调用继承法(对象冒充)

调用继承的本质是,在子类的构造器中,让父类的构造器方法在子类的执行上下文上执行,父类构造器方法上所有通过 this方式操作的内容实际上都都是操作的子类的实例化对象上的内容。因此,这种做法仅仅为了减少重复代码的编写。  

       

function parentClass() {

    // private field
    var x = "I‘m a parentClass field!";

    // private method
    function method1() {
        alert(x);

        alert("I‘m a parentClass method!");

    }
    // public field
    this.x = "I‘m a parentClass object field!";

    // public method
    this.method1 = function() {

        alert(x);

        alert(this.x);

        method1();

    }
}

parentClass.prototype.method = function () {

    alert("I‘m a parentClass prototype method!");

} 
parentClass.staticMethod = function () {

    alert("I‘m a parentClass static method!");

}
function subClass() {
    // inherit
    parentClass.call(this);       //通过父类的 call方法,将子类的 this 指针传入。使父类方法在子类上下文中执行。这样,父类中的所有在父类内部通过 this方式定义的公有实例成员都会被子类继承。
    // private field
    var x = "I‘m a subClass field!";

    // private method
    function method2() {
        alert(x);

        alert("I‘m a subClass method!");

    }

    // public field
    this.x = "I‘m a subClass object field!";
    // public method
    this.method2 = function() {

        alert(x);

        alert(this.x);
        method2();
    }
   this.method3 = function() {
        method1();
    }
} 
// test
var o = new subClass(); 
alert(o instanceof parentClass);    // false
alert(o instanceof subClass);       // true 
//通过call 继承,子类的实例化对象只属于子类,不属于父类。
alert(o.constructor);  // function subClass() {...} 同原型继承,constructor属性属于子类
o.method1();    // I‘m a parentClass field!

                // I‘m a subClass object field!

                // I‘m a parentClass field!
                // I‘m a parentClass method!

//父类中继承来的公有变量,方法,都可以被访问到。

o.method2();    // I‘m a subClass field!

                // I‘m a subClass object field!
                // I‘m a subClass field!

                // I‘m a subClass method! 
//子类可以随意调用自己的方法和变量

o.method();                // error定义在父类原型上的方法,不会被子类继承。

o.method3();               //error 子类中定义的实例方法同样不能访问父类中定义的私有实例成员的。

subClass.staticMethod();   //error 静态成员同样不会被继承的

PS思考下面的代码,得出两点:

alert(o instanceof parentClass);    // false

alert(o instanceof subClass);       // true 

对比原型继承,这里的 o不属于父类,为什么,我认为call调用这种方法不是真正意义上的继承,就是“调用”,也有另一种叫法:对象冒充,即不是真正的继承,真实可以通过这个途径访问到到另一类的共有方法,变量。所以子类对象不属于父类;这是其一,不同的其二:通过调用继承法,可以实现多继承。既然是调用,那么一个类调用多个类是OK的。一个子类可以从多个父类中继承通过 this 方式定义在父类内部的所有公有实例成员。 


两种方法对比

原型继承:只继承属性.不能传参,不能共享?Son.Prototype=newfather();

调用继承(对象冒充):只能继承构造函数中的信息(传参),不能继承属性(共享)。father.call(this,pro1,pro2);

小结:在实际应用当中,我们可以根据的自己的不同需要,可以将两种方法结合来使用,满足不同方法,原型的的访问,这样即达到了属性共享,方法又独立的目的。有这么几种:

组合模式(原型+构造函数)(属性共享,方法独立)

   寄生式继承(原型式+工厂方法)



javascript面向对象二-“继承”,布布扣,bubuko.com

javascript面向对象二-“继承”

上一篇:Spring MVC 原理 - DispatcherServlet调用完整过程(下)


下一篇:高性能javascript注意事项