javascript继承(四)—prototype属性介绍

js里每个function都有一个prototype属性,而每个实例都有constructor属性,而且每个function的prototype都有一个constructor属性,这个属性会指向自身。这会造成一个很是有意思的链式结构。举例以下:javascript

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);

输出结果以下:html

image

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

image

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

经过前面的两篇文章 测试

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

javascript建立对象的三种模式spa

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

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

方案一:htm

直接将父类的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。因此会出现这种情形。

通过测试,这种继承方式是可行的。使用这种方式继承,能够看到基本实现了子类继承父类的全部属性和方法,而且子类的构造函数还是子类,父类的构造函数是父类。自认为这是比较完美的方案。

相关文章
相关标签/搜索