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。因此會出現這種情形。

通過測試,這種繼承方式是可行的。使用這種方式繼承,能夠看到基本實現了子類繼承父類的全部屬性和方法,而且子類的構造函數還是子類,父類的構造函數是父類。自認爲這是比較完美的方案。

相關文章
相關標籤/搜索