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

