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

經過測試,這種繼承方式是可行的。使用這種方式繼承,可以看到基本實現了子類繼承父類的所有屬性和方法,並且子類的構造函數仍是子類,父類的構造函數是父類。自認為這是比較完美的方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM