zoukankan      html  css  js  c++  java
  • javascript继承(四)—prototype属性介绍

    js里每一个function都有一个prototype属性,而每一个实例都有constructor属性,并且每一个function的prototype都有一个constructor属性,这个属性会指向自身。这会形成一个非常有意思的链式结构。举例如下:

    function Person(){
        this.name =12;
    }
    console.log(Person.prototype);
    console.log(Person.prototype.constructor);//输出Person,指向自身
    console.log(Person.prototype.constructor.prototype.constructor);//输出Person,指向自身
    /***再看一下这个类的输出,则会出现如下情况**/
    function Person(){}
    Person.prototype.name = 'xiaoming';
    var p1 = new Person();
    console.log(p1);

    输出结果如下:

    image

    会把这个实例显示出来,展开如下。p1有一个原型属性,这个属性有一个构造方法Person(),而这个构造方法又有prototype属性,这个属性有constructor方法…

    image

    这里主要让我们了解一下prototype是属于类(或者说函数function)的属性,指向这个类的共有属性和方法,而constructor是实例的属性,指向它的构造函数(也可以说是类,js里构造函数和类是一个概念)。

    通过前面的两篇文章

    javascript继承—继承的实现原理(1)

    javascript创建对象的三种模式

    我们知道用prototype来实现继承可以使子类拥有父类的共有属性和方法,其它两种不行。所以这里主要讨论如何用prototype实现继承。

    由于采用prototype继承父类的实例在javascript继承—继承的实现原理(1)中已有论述,下面着重介绍用prototype继承实现的几种方式。

    方案一:

    直接将父类的prototype属性赋给子类,同时用call继承父类的特权属性,然后再修改子类prototype的constructor

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    
    Person.prototype = {
        sayHi:function(){
            alert('hi');
        }
    }
    
    function Student(name,age,grade){
        Person.call(this,name,age);
        this.grade = grade;
    }
    
    Student.prototype = Person.prototype; 
    //Student.prototype.constructor = Student;
    Student.prototype.study = function(){
        alert('study');
    }
    var p1 = new Person('xiaoming',10);
    var s1 = new Student('xiaohong',9,3);
    //p1.study();// p1.study is not a function 说明Person的实例没有study方法,即子类的共有方法没有被父类共享
    console.log(p1);//Person { name="xiaoming", age=10, sayHi=function()}
    console.log(s1);//Student { name="xiaohong", age=9, grade=3, 更多...}
    console.log(p1.constructor);//Object() 
    console.log(s1.constructor);//Object() 子类父类实例相同都为Object
    
    /**
    如果在原文中加上Student.prototype.constructor = Student;
    则
    console.log(p1.constructor);//Student() 
    console.log(s1.constructor);//Student() 子类父类实例相同都为Student
    ***/

    这种方案经测试是行不通的,因为不管怎么变,子类和父类的实例都会共有相同的constructor,这种情形下修改子类的共有方法,同时会修改了父类的共有方法,说明此法不通。

    方案二:

    将父类的实例赋给子类的原型对象,同时使用call方法使子类继承父类的特权属性。

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    
    Person.prototype = {
        constructor:Person,
        sayHi:function(){
            alert('hi');
        }
    }
    
    function Student(name,age,grade){
        Person.call(this,name,age);
        this.grade = grade;
    }
    
    Student.prototype = new Person();
    Student.prototype.constructor = Student;
    Student.prototype.study = function(){
        alert('study');
    }
    var p1 = new Person('xiaoming',10);
    var s1 = new Student('xiaohong',9,3);
    //p1.study();// p1.study is not a function 说明Person的实例没有study方法,即子类的共有方法没有被父类共享
    console.log(p1);//Person { name="xiaoming", age=10, sayHi=function()}
    console.log(s1);//Student { name="xiaohong", age=9, grade=3, 更多...}
    console.log(p1.constructor);//Person(name,age) 父类的实例指向仍是父类
    console.log(s1.constructor);//Student(name,age,grade) //子类的实例指向仍是子类

    得到的结果基本符合我们继承的要求,但是这个继承实现方式所继承的是父类实例所有的属性和方法,即实例方法(也可以说是特权方法),每创建一个子类对象都会把父类的特权方法都复制一遍,这样会耗费资源并且是无意义的。这时创建子类的实例就相当于javascript创建对象的三种模式 中的第二种构造函数模式。

    方案三:

    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    //第一种创建共有方法方式
    Person.prototype.sayHi = function(){
        alert('hi');
    }
    //第二种创建共有方法方式
    /*--------------------------------------------------------
    Person.prototype = {
        constructor:Person,
        sayHi:function(){
            alert('hi');
        }
    }
    -------------------------------------------------------*/
    
    function Student(name,age,grade){
        Person.call(this,name,age);
        this.grade = grade;
    }
    
    for(var i in Person.prototype){Student.prototype[i] = Person.prototype[i]}
    
    //第二种创建共有方法方式继承时需要加上这句,不然子类实例会指向Person
    /*--------------------------------------------------------
    Student.prototype.constructor = Student
    -------------------------------------------------------*/
    Student.prototype.study = function(){
        alert('study');
    }
    var p1 = new Person('xiaoming',10);
    var s1 = new Student('xiaohong',9,3);
    //p1.study();// p1.study is not a function 说明Person的实例没有study方法,即子类的共有方法没有被父类共享
    console.log(p1);//Person { name="xiaoming", age=10, sayHi=function()}
    console.log(s1);//Student { name="xiaohong", age=9, grade=3, 更多...}
    console.log(p1.constructor);//Person(name,age) 父类的实例指向仍是父类
    console.log(s1.constructor);//Student(name,age,grade) //子类的实例指向仍是子类
    /*--------------------------------------------------------
    第二种方式
    console.log(p1.constructor);//Person() 父类的实例指向仍是Person
    -------------------------------------------------------*/

    用prototype实现原型链继承。对于第三种创建共有方法,如果创建的时候不加constructor: Person,得到的父类实例会指向Object,是因为创建共有方法的时候直接将一个包含共有方法的Object对象赋给了父类的prototype属性,将父类原有的constructor属性Person修改为Object。所以会出现这种情形。

    经过测试,这种继承方式是可行的。使用这种方式继承,可以看到基本实现了子类继承父类的所有属性和方法,并且子类的构造函数仍是子类,父类的构造函数是父类。自认为这是比较完美的方案。

  • 相关阅读:
    《Java数据结构与算法》笔记CH43用栈实现分隔符匹配
    《Java数据结构与算法》笔记CH2无序数组
    《Java数据结构与算法》笔记CH3简单排序
    《Java数据结构与算法》笔记CH41栈的实现
    《Java数据结构与算法》笔记CH42用栈实现字符串反转
    java流程控制.3循环结构
    java方法.1方法的定义和调用
    java流程控制.1scanner
    java方法.2方法的重载
    java方法.3递归
  • 原文地址:https://www.cnblogs.com/lj915/p/3782425.html
Copyright © 2011-2022 走看看