這兩天真的一直在看原型以及繼承之間的千絲萬縷,哇,收獲頗多,不過所謂溫故知新,還是要多復習復習知識點,才能察覺那些之前不易發現的小小sparkle
真心推薦MDN文檔——對象原型,JavaScript 中的繼承,文檔指出了很多原理性的東西,所謂飲水思源,大家千萬不要輕易就相信別人說什么好哇就像挖到寶藏一樣,要多實踐,多探索原理性的東西你才能真正把它掌握,它才真的是你的東西。
期間我還參考了阮一峰老師的Javascript 面向對象編程(一):封裝,Javascript面向對象編程(二):構造函數的繼承
Javascript面向對象編程(三):非構造函數的繼承
廖雪峰老師的原型繼承
在MDN文檔以及廖雪峰老師那里都提到了構造函數繼承的一種方法:(這里主要說明繼承的一個辦法,原理性的說明請看以上推薦鏈接)
function Person(first, last, age, gender, interests) {this.name = {first,last};this.age = age;this.gender = gender;this.interests = interests;
};Person.prototype.greeting = function() {alert('Hi! I\'m ' + this.name.first + '.');
};
這里先說明一下,在構造函數中,屬性最好都寫在構造函數里,而方法最后都寫在prototype(原型對象)里,這樣層次比較分明,但是如果是常屬性(值不變),那么他可以寫進prototype里,這樣可以減少內存的占據
我們現在要創建一個Teacher類,需要繼承Person的所有屬性和方法,
同時也包括:
一個新的屬性subject
——這個屬性包含了教師教授的學科。
一個被更新的greeting()
方法,這個方法打招呼聽起來比一般的greeting()
方法更正式一點——對于一個教授一些學生的老師來說。
MDN文檔的做法是:
function Teacher(first, last, age, gender, interests, subject) {// 調用person構造函數,綁定this變量:Person.call(this, first, last, age, gender, interests); this.subject = subject;
}Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;
?而這里廖雪峰老師的做法的第一步也是(與廖老師文章里例子不同但原理相似)
Person.call(this, first, last, age, gender, interests); this.subject = subject;
之后他指出,調用了Person構造函數不等于繼承了Person,Teacher
創建的對象的原型是:
new Teacher() ----> Teacher.prototype ----> Object.prototype ----> null
必須想辦法把原型鏈修改為:
new Teacher() ----> Teacher.prototype ----> Person.prototype ----> Object.prototype ----> null
我們必須借助一個中間對象來實現正確的原型鏈,這個中間對象的原型要指向Person.prototype
。為了實現這一點,參考道爺(就是發明JSON的那個道格拉斯)的代碼,中間對象可以用一個空函數F
來實現:?
// 空函數F:
function F() {
}// 把F的原型指向Student.prototype:
F.prototype = Person.prototype;// 把Teacher的原型指向一個新的F對象,F對象的原型正好指向Person.prototype:
Teacher.prototype = new F();// 把PrimaryStudent原型的構造函數修復為Teacher:
Teacher.prototype.constructor = Teacher;
?
重寫Teacher的greeting函數:?
Teacher.prototype.greeting = function() {var prefix;if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {prefix = 'Mr.';} else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {prefix = 'Mrs.';} else {prefix = 'Mx.';}alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};
驗證:?
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');teacher1.name.first;
teacher1.interests[0];
teacher1.bio();
teacher1.subject;
teacher1.greeting();
?廖老師用空函數的方法實現了間接繼承又不占用多少空間,Teacher.prototype的改寫也不會影響上一級的prototype,妙也!
但是為什么MDN文檔用
Teacher.prototype = Object.create(Person.prototype);
就基本全搞定繼承了呢?
帶著好奇我查了這個函數的原理
Object.create = function (o) {var F = function () {};F.prototype = o;return new F();};
哈哈哈哈,這不是跟廖老師的方法一樣嘛,也是造一個空函數,然后間接實現函數的繼承!
?
而非構造函數中,阮老師總結的第一個方法 object()方法,其原理類似于Object.create()
function object(o) {function F() {}F.prototype = o;return new F();}
?
var Chinese = {nation:'China',greeting:function (name) {alert('Hi! I\'m ' + name+' from '+this.nation+ '.');}};var Doctor =Object.create(Chinese);Doctor.career='doctor';Doctor.greeting=function(name){alert('Hi! I\'m a ' + this.career+' from '+this.nation+ ',my name is '+name+ ' .');};alert(Doctor.nation);//ChinaDoctor.greeting('Emma');//Hi! I'm a doctor from China,my name is Emma .
大家可以試試看。?
好的,總結到此啦,阮一峰老師提到了很多構造函數的繼承方法,但是各有利弊吧,原型以及繼承這塊‘深海‘’還是需要我們多去翻滾才行,這里順便拋一個new方法原理,自己好好瞎捉摸吧哈哈哈哈