zoukankan      html  css  js  c++  java
  • javascript代码复用--继承

    javascript代码复用--继承

    由于javascript没有类的概念,因此无法通过接口继承,只能通过实现继承。实现继承是继承实际的方法,javascript中主要是依靠原型链要实现。

    原型链继承

    原型链继承是基本的继承模式,其本质是重写原型对象,使其为新对象的实例。代码实现如下:

    复制代码
    function Person(){
    
        this.name = "default";
    
        var temp = "temp";
    
    }
    
    Person.prototype.age=0;
    
    Person.prototype.getName = function(){
    
        return this.name;
    
    }
    
    Person.prototype.getAge = function(){
    
        return this.age;
    
    }
    
    console.log(Person.prototype.age);//0
    
    console.log(Person.age);//undefined
    
    console.log(Person.prototype.name);//undefined
    
    console.log(Person.name);//Person, if other property, should be undefined
    
    function Student(){
    
        this.type = "student";
    
    }
    
    //inheritance
    
    Student.prototype = new Person();
    
    console.log(Student.prototype.constructor);//Person(){}
    
    console.log(Student.prototype.name);//default
    
    Student.prototype.constructor = Student;
    
    var student1 = new Student();
    
    console.log(student1.getName());//default
    
    console.log(student1.name);//default
    
    console.log(student1.getAge());//0
    
    console.log(student1.age);//0
    
    console.log(student1.__proto__.age);//0
    
    console.log(student1.temp);//undefined
    
    console.log(student1 instanceof Object);//true
    
    console.log(student1 instanceof Person);//true
    
    console.log(student1 instanceof Student);//true
    
    console.log(Student instanceof Person);//false
    复制代码

    以上代码主要注意两个问题:

    1.函数局部变量,内部属性及原型属性的区别。var temp定义了一个局部变量,this.name定义了一个内部属性,prototype.age则定义了一个原型属性。

    对于局部变量,无法在函数以外的地方调用,包括实例。

    之前说过,函数本身的prototype属性仅仅用于函数实例的属性继承,而函数本身不会使用这个关联的prototype,在prototype中设置的属性将直接作用于所有实例。(比如Person的实例Student.prototype和student1,注意Student并不是Person的实例

    而对于函数内部属性,函数实例将直接拥有对应的内部属性(初始值),而无法通过函数本身使用内部属性。这一点其实跟prototype属性有所区别。

    2.利用重写原型对象实现继承的时候,Student.prototype = new Person(), Student.prototype将指向了另一个对象Person.prototype,因此此时Student.prototype.constructor将指向Person函数。通过Student.prototype.constructor = Student 可以将其constructor重新指向Student。

    通过原型链可以更好的理解上面的代码:

    clip_image001

    原型链继承的缺点

    关于原型链继承的问题,其实就是跟通过原型方式创建对象的问题一样,就是原型中包含引用类型所带来的共享问题。

    还有就是创建实例的时候,无法向构造器中传递参数。

    构造函数继承

    另一种经典的继承便是通过构造函数实现继承,即通过apply()和call()方法在子类构造函数内部调用父类构造函数。具体实现如下:

    复制代码
    function Person(name){
    
        this.name = name;
    
        this.friends = new Array();
    
    }
    
    Person.prototype.age = 0;
    
    function Student(name){
    
        Person.call(this, name);
    
    }
    
    var student1 = new Student("Huge");
    
    student1.friends.push("Alan");
    
    console.log(student1.name);//Huge
    
    console.log(student1.age);//undefined
    
    console.log(student1.friends);//["Alan"]
    
    var student2 = new Student("Heri");
    
    student2.friends.push("Amly");
    
    console.log(student2.name);//Heri
    
    console.log(student2.friends);//["Amly"]
    
    console.log(student1 instanceof Person);//false
    
    console.log(student1 instanceof Student);//true
    复制代码

    通过构造函数继承的问题除了构造函数模式本身存在的缺点之外(重复实例化方法),也无法类型识别,因此在父类原型中定义的方法和属性无法在子类中调用。

    组合继承

    由于通过原型链继承和构造函数继承都有其优缺点,因此将这两种继承方式组合起来,使用原型链继承实现原型中方法和属性的继承,通过构造函数继承实现参数传递和引用类型继承,是javascript中最常用的继承模式。代码实现如下:

    复制代码
    function Person(name, age){
    
        this.name = name;
    
        this.age = age;
    
        this.friends = new Array();
    
    }
    
    Person.prototype.getName = function(){
    
        return this.name;
    
    }
    
    function Student(name, age){
    
        this.type = "student";
    
        Person.call(this, name, age);
    
    }
    
    Student.prototype = new Person();
    
    Student.prototype.constructor = Student;
    
    var student1 = new Student("Huge", 15);
    
    student1.friends.push("Alan");
    
    console.log(student1.name);//Huge
    
    console.log(student1.age);//15
    
    console.log(student1.friends);//["Alan"]
    
    console.log(student1.getName());//Huge
    
    console.log(student1 instanceof Person);//true
    
    console.log(student1 instanceof Student);//true
    
    var student2 = new Student("Heri", 16);
    
    student2.friends.push("Amly");
    
    console.log(student2.name);//Heri
    
    console.log(student2.age);//16
    
    console.log(student2.friends);//["Amly"]
    
    console.log(Student.prototype.name);//undefined
    
    console.log(Student.prototype.friends);//[]
    复制代码

    从代码可以看出,组合继承会调用两次父类的构造函数:创建子类原型的时候和在子类构造函数内部调用。实际上,第一次创建子类原型的时候,子类已经包含了父类对象的全部实例属性,因此当通过调用子类构造函数创建实例的时候,将会重写这些属性。即同时存在两组属性,一组在实例上,一组在子类原型中,如上代码中Student.prototype.friends返回的空数组。这就是调用两次父类构造函数的结果

    其他继承方式

    Crockford曾经提出了prototypal inheritance以及与之结合的parasitic inheritance。通过原型创建基于原有对象的新对象,并为新对象增加功能。

    复制代码
    //prototypal inhertance
    
    function createObject(obj){
    
        function F(){}
    
        F.prototype = obj;
    
        return new F();
    
    }
    
    //parasitic inheritance
    
    function enhanceObject(obj){
    
        var enhanceObj = createObject(obj);
    
        enhanceObj.getName = function(){
    
           return this.name;
    
        }
    
        return enhanceObj;
    
    }
    
    var person = {
    
        name : "Alan"
    
    };
    
    var person1 = enhanceObject(person);
    
    console.log(person1.getName());//Alan
    复制代码

    更进一步,为了避免组合继承模式两次调用父类构造函数的问题,可以利用parasitic inheritance来继承父类的原型,再将其指定给子类的原型。如下代码:

    复制代码
    //prototypal inhertance
    
    function createObject(obj){
    
        function F(){}
        
        F.prototype = obj;
    
        return new F();
    
    }
    
    //parasitic inheritance
    
    function inheritPrototype(superObj, subObj){
    
        var obj = createObject(superObj.prototype);
    
        obj.constructor = subObj;
    
        subObj.prototype = obj;
    
    }
    
    function Person(name, age){
    
        this.name = name;
    
        this.age = age;
    
        this.friends = new Array();
    
    }
    
    Person.prototype.getName = function(){
    
        return this.name;
    
    }
    
    function Student(name, age){
    
        this.type = "student";
    
        Person.call(this, name, age);
    
    }
    
    inheritPrototype(Person, Student);
    
    var student1 = new Student("Huge", 15);
    
    student1.friends.push("Alan");
    
    console.log(student1.name);//Huge
    
    console.log(student1.age);//15
    
    console.log(student1.friends);//["Alan"]
    
    console.log(student1.getName());//Huge
    
    console.log(student1 instanceof Person);//true
    
    console.log(student1 instanceof Student);//true
    
    var student2 = new Student("Heri", 16);
    
    student2.friends.push("Amly");
    
    console.log(student2.name);//Heri
    
    console.log(student2.age);//16
    
    console.log(student2.friends);//["Amly"]
    
    console.log(Student.prototype.name);//undefined
    
    console.log(Student.prototype.friends);//undefined
    复制代码

    可以看出,子类只调用了父类一次构造函数,避免在子类原型中创建不必要的属性。同时,原型链也保持不便,可以说是实现类型继承的最有效方式。

     
    分类: Javascript
  • 相关阅读:
    根据第三方库spire.pdf使用指定打印机打印pdf文件
    大批量GPS坐标转百度坐标
    maven settings.xml
    linux 权限
    hyper-v 创建ubuntu虚拟机设置静态ip
    mysql 复制
    nginx
    python函数定义
    Mysql索引浅析
    Mysql 数据库锁机制浅析
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3168823.html
Copyright © 2011-2022 走看看