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
會把這個實例顯示出來,展開以下。p1有一個原型屬性,這個屬性有一個構造方法Person(),而這個構造方法又有prototype屬性,這個屬性有constructor方法…java
這裏主要讓咱們瞭解一下prototype是屬於類(或者說函數function)的屬性,指向這個類的共有屬性和方法,而constructor是實例的屬性,指向它的構造函數(也能夠說是類,js裏構造函數和類是一個概念)。函數
經過前面的兩篇文章 測試
咱們知道用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。因此會出現這種情形。
通過測試,這種繼承方式是可行的。使用這種方式繼承,能夠看到基本實現了子類繼承父類的全部屬性和方法,而且子類的構造函數還是子類,父類的構造函數是父類。自認爲這是比較完美的方案。