[js高手之路]从原型链开始图解继承到组合继承的产生
于javascript原型鏈的層層遞進查找規則,以及原型對象(prototype)的共享特性,實現繼承是非常簡單的事情
一、把父類的實例對象賦給子類的原型對象(prototype),可以實現繼承
1 function Person(){ 2 this.userName = 'ghostwu'; 3 } 4 Person.prototype.showUserName = function(){ 5 return this.userName; 6 } 7 function Teacher (){} 8 Teacher.prototype = new Person(); 9 10 var oT = new Teacher(); 11 console.log( oT.userName ); //ghostwu 12 console.log( oT.showUserName() ); //ghostwu通過把父類(Person)的一個實例賦給子類Teacher的原型對象,就可以實現繼承,子類的實例就可以訪問到父類的屬性和方法
如果你不會畫這個圖,你需要去看下我的這篇文章:[js高手之路]一步步圖解javascript的原型(prototype)對象,原型鏈
第11行,執行oT.userName, 首先去oT對象上查找,很明顯oT對象上沒有任何屬性,所以就順著oT的隱式原型__proto__的指向查找到Teacher.prototype,
發現還是沒有userName這個屬性,繼續沿著Teacher.prototype.__proto__向上查找,找到了new Person() 這個實例上面有個userName,值為ghostwu
所以停止查找,輸出ghostwu.
第12行,執行oT.showUserName前面的過程同上,但是在new Person()這個實例上還是沒有查找到showUserName這個方法,繼續沿著new Person()的
隱式原型__proto__的指向( Person.prototype )查找,在Person.prototype上找到了showUserName這個方法,停止查找,輸出ghostwu.
二、把父類的原型對象(prototype)賦給子類的原型對象(prototype),可以繼承到父類的方法,但是繼承不到父類的屬性
1 function Person(){ 2 this.userName = 'ghostwu'; 3 } 4 Person.prototype.showUserName = function(){ 5 return 'Person::showUserName方法'; 6 } 7 function Teacher (){} 8 Teacher.prototype = Person.prototype; 9 10 var oT = new Teacher(); 11 console.log( oT.showUserName() ); //ghostwu 12 console.log( oT.userName ); //undefined, 沒有繼承到父類的userName因為Teacher.prototype被Person.protoype替換了( 第8行代碼 ),所以,Teacher的prototype屬性就直接指向了Person.prototype. 所以獲取不到Person實例的屬性
三、發生繼承關系后,實例與構造函數(類)的關系判斷
還是通過instanceof和isPrototypeOf判斷
1 function Person(){ 2 this.userName = 'ghostwu'; 3 } 4 Person.prototype.showUserName = function(){ 5 return this.userName; 6 } 7 function Teacher (){} 8 Teacher.prototype = new Person(); 9 10 var oT = new Teacher(); 11 console.log( oT instanceof Teacher ); //true 12 console.log( oT instanceof Person ); //true 13 console.log( oT instanceof Object ); //true 14 console.log( Teacher.prototype.isPrototypeOf( oT ) ); //true 15 console.log( Person.prototype.isPrototypeOf( oT ) ); //true 16 console.log( Object.prototype.isPrototypeOf( oT ) ); //true四,父類存在的方法和屬性,子類可以覆蓋(重寫),子類沒有的方法和屬性,可以擴展
1 function Person() {} 2 Person.prototype.showUserName = function () { 3 console.log('Person::showUserName'); 4 } 5 function Teacher() { } 6 Teacher.prototype = new Person(); 7 Teacher.prototype.showUserName = function(){ 8 console.log('Teacher::showUserName'); 9 } 10 Teacher.prototype.showAge = function(){ 11 console.log( 22 ); 12 } 13 var oT = new Teacher(); 14 oT.showUserName(); //Teacher::showUserName 15 oT.showAge(); //22五、重寫原型對象之后,其實就是把構造函數的原型屬性(prototype)的指向發生了改變
構造函數的原型屬性(prototype)的指向發生了改變,會把原本的繼承關系覆蓋(切斷)
1 function Person() {} 2 Person.prototype.showUserName = function () { 3 console.log('Person::showUserName'); 4 } 5 function Teacher() {} 6 Teacher.prototype = new Person(); 7 Teacher.prototype = { 8 showAge : function(){ 9 console.log( 22 ); 10 } 11 } 12 var oT = new Teacher(); 13 oT.showAge(); //22 14 oT.showUserName();上例,第7行,Teacher.prototype重寫了Teacher的原型對象(prototype),原來第6行的Teacher構造函數的prototype屬性指向new Person()的關系切斷了
所以在第14行,oT.showUserName() 就會發生調用錯誤,因為Teacher構造函數上的prototype屬性不再指向父類(Person)的實例,繼承關系被破壞了.
六、在繼承過程中,小心處理實例的屬性上引用類型的數據
1 function Person(){ 2 this.skills = [ 'php', 'javascript' ]; 3 } 4 function Teacher (){} 5 Teacher.prototype = new Person(); 6 7 var oT1 = new Teacher(); 8 var oT2 = new Teacher(); 9 oT1.skills.push( 'linux' ); 10 console.log( oT2.skills ); //php, java, linuxoT1的skills添加了一項linux數據,其他的實例都能訪問到,因為其他實例中共享了skills數據,skills是一個引用類型
七、借用構造函數
為了消除引用類型影響不同的實例,可以借用構造函數,把引用類型的數據復制到每個對象上,就不會相互影響了
1 function Person( uName ){ 2 this.skills = [ 'php', 'javascript' ]; 3 this.userName = uName; 4 } 5 Person.prototype.showUserName = function(){ 6 return this.userName; 7 } 8 function Teacher ( uName ){ 9 Person.call( this, uName ); 10 } 11 var oT1 = new Teacher(); 12 oT1.skills.push( 'linux' ); 13 var oT2 = new Teacher(); 14 console.log( oT2.skills ); //php,javascript 15 console.log( oT2.showUserName() );?雖然oT1.skills添加了一項Linux,但是不會影響oT2.skills的數據,通過子類構造函數中call的方式,去借用父類的構造函數,把父類的屬性復制過來,而且還能
傳遞參數,如第8行,但是第15行,方法調用錯誤,因為在構造中只復制了屬性,不會復制到父類原型對象上的方法
八、組合繼承(原型對象+借用構造函數)
經過以上的分析, 單一的原型繼承的缺點有:
1、不能傳遞參數,如,
Teacher.prototype = new Person();有些人說,小括號后面可以跟參數啊,沒錯,但是只要跟了參數,子類所有的實例屬性,都是跟這個一樣,說白了,還是傳遞不了參數
2、把引用類型放在原型對象上,會在不同實例上產生相互影響
單一的借用構造函數的缺點:
1、不能復制到父類的方法
剛好原型對象方式的缺點,借用構造函數可以彌補,借用構造函數的缺點,原型對象方式可以彌補,于是,就產生了一種組合繼承方法:
1 function Person( uName ){ 2 this.skills = [ 'php', 'javascript' ]; 3 this.userName = uName; 4 } 5 Person.prototype.showUserName = function(){ 6 return this.userName; 7 } 8 function Teacher ( uName ){ 9 Person.call( this, uName ); 10 } 11 Teacher.prototype = new Person(); 12 13 var oT1 = new Teacher( 'ghostwu' ); 14 oT1.skills.push( 'linux' ); 15 var oT2 = new Teacher( 'ghostwu' ); 16 console.log( oT2.skills ); //php,javascript 17 console.log( oT2.showUserName() ); //ghostwu子類實例oT2的skills不會受到oT1的影響,子類的實例也能調用到父類的方法.
總結
以上是生活随笔為你收集整理的[js高手之路]从原型链开始图解继承到组合继承的产生的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java.lang包【Object类】
- 下一篇: servlet请求和响应的过程