淺談JavaScript 繼承機制的實現

對象冒充的方法實現:

[javascript]
function Human() { <span style="white-space:pre"> </span>//定義Human類  
    this.species = "Human"; 

function Sex(sex) { <span style="white-space:pre">    </span>//定義Sex類  
    this.sex = sex; 

function Chinese(name,sex) { 
    this.name = name; 
    this.newMethod1 = Human;    //對象冒充,指向Human對象  
    this.newMethod1();          //調用方法,實現繼承  
    delete this.newMethod1; //刪除該對象的引用,避免錯誤調用  
     
    this.newMethod2 = Sex;      //對象冒充,指向Sex對象  
    this.newMethod2(sex);       //調用方法,實現繼承  
    delete this.newMethod2; //刪除該對象的引用,避免錯誤調用  

var chin1 = new Chinese("小明","Male"); 
function Human() { <span style="white-space:pre"> </span>//定義Human類
 this.species = "Human";
}
function Sex(sex) { <span style="white-space:pre"> </span>//定義Sex類
 this.sex = sex;
}
function Chinese(name,sex) {
 this.name = name;
 this.newMethod1 = Human;   //對象冒充,指向Human對象
 this.newMethod1();   //調用方法,實現繼承
 delete this.newMethod1; //刪除該對象的引用,避免錯誤調用
 
 this.newMethod2 = Sex;   //對象冒充,指向Sex對象
 this.newMethod2(sex);  //調用方法,實現繼承
 delete this.newMethod2; //刪除該對象的引用,避免錯誤調用
}
var chin1 = new Chinese("小明","Male");

 

對象冒充的方法很簡單易懂,原理就是利用方法的調用,在函數內實現屬性的定義.

而且,對象冒充還能實現多繼承.但也有不好地方.

如下:

[javascript]
<script type="text/javascript"> 
function Human(){ <span style="white-space:pre">  </span>//定義Human類  
    this.species = "Human"; 

function Sex(sex) { <span style="white-space:pre">    </span>//定義Sex類  
    this.sex = sex; 
    this.species = "Animal"; //因為調用順序,會替換Human類的species屬性  

function Chinese(name,sex){ 
    this.name = name; 
    this.newMethod1 = Human;    //對象冒充,指向Human對象  
    this.newMethod1();          //調用方法,實現繼承  
    delete this.newMethod1; //刪除該對象的引用,避免錯誤調用  
     
    this.newMethod2 = Sex;      //對象冒充,指向Sex對象  
    this.newMethod2(sex);       //調用方法,實現繼承  
    delete this.newMethod2; //刪除該對象的引用,避免錯誤調用  

var chin1 = new Chinese("小明","Male"); 
<script type="text/javascript">
function Human(){ <span style="white-space:pre"> </span>//定義Human類
 this.species = "Human";
}
function Sex(sex) { <span style="white-space:pre"> </span>//定義Sex類
 this.sex = sex;
 this.species = "Animal"; //因為調用順序,會替換Human類的species屬性
}
function Chinese(name,sex){
 this.name = name;
 this.newMethod1 = Human;   //對象冒充,指向Human對象
 this.newMethod1();   //調用方法,實現繼承
 delete this.newMethod1; //刪除該對象的引用,避免錯誤調用
 
 this.newMethod2 = Sex;   //對象冒充,指向Sex對象
 this.newMethod2(sex);  //調用方法,實現繼承
 delete this.newMethod2; //刪除該對象的引用,避免錯誤調用
}
var chin1 = new Chinese("小明","Male");

 

因為是通過調用函數來"繼承"的,如果多繼承時,父類出現同名屬性時,會被優先級高的替代.

例如上面的代碼中,Sex類會替換Human類的同名屬性.

 

也可以通過call()和apply()方法來實現繼承,

其實原理和對象冒充一樣.

[javascript]
function Human(){       //定義Human類  
    this.species = "Human"; 

function Sex(sex) {     //定義Sex類  
    this.sex = sex; 

function Chinese(name,sex){ 
    this.name = name; 
    Human.call(this);  <span style="white-space:pre"> </span>//call()方法  
    Sex.apply(this,[sex]);<span style="white-space:pre">  </span>//apply()方法  

var chin1 = new Chinese("小明","Male"); 
function Human(){   //定義Human類
 this.species = "Human";
}
function Sex(sex) {  //定義Sex類
 this.sex = sex;
}
function Chinese(name,sex){
 this.name = name;
 Human.call(this);  <span style="white-space:pre"> </span>//call()方法
 Sex.apply(this,[sex]);<span style="white-space:pre"> </span>//apply()方法
}
var chin1 = new Chinese("小明","Male");
這裡是call()和apply()方法的介紹:https://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp

其實對象冒充最大的問題就是,無法通過子類找到繼承的父類.

所以這並非真正意義的繼承.

[javascript]
chin1 instanceof Chinese;<span style="white-space:pre">   </span>//true  
chin1 instanceof Human;<span style="white-space:pre">     </span>//false  
chin1 instanceof Sex;<span style="white-space:pre">       </span>//false 
chin1 instanceof Chinese;<span style="white-space:pre"> </span>//true
chin1 instanceof Human;<span style="white-space:pre">  </span>//false
chin1 instanceof Sex;<span style="white-space:pre">  </span>//false

 

當繼承的父類中有定義對應的方法時,每次定義的對象都會重新生成一個對應的方法,這樣十分浪費內存,而且不便於管理.

[javascript]
function Human(){       //定義Human類  
    this.species = "Human"; 
    this.fun = function() {}; 

function Sex(sex) {     //定義Sex類  
    this.sex = sex; 

function Chinese(name,sex){ 
    this.name = name; 
    Human.call(this);   //call()方法  
    Sex.apply(this,[sex]);  //apply()方法  

var chin1 = new Chinese("小明","Male"); 
var chin2 = new Chinese("小紅","Female"); 
chin1.fun === chin2.fun; //false 
function Human(){   //定義Human類
 this.species = "Human";
 this.fun = function() {};
}
function Sex(sex) {  //定義Sex類
 this.sex = sex;
}
function Chinese(name,sex){
 this.name = name;
 Human.call(this);   //call()方法
 Sex.apply(this,[sex]); //apply()方法
}
var chin1 = new Chinese("小明","Male");
var chin2 = new Chinese("小紅","Female");
chin1.fun === chin2.fun; //false

 

因此,下面要討論的是原型繼承(prototype).
[javascript]
function Human(){       //定義Human類  
    this.species = "Human"; 

function Chinese(name){ 
    this.name = name; 

Chinese.prototype = new Human(); //原型對象指向Human類  
Chinese.prototype.constructor = Chinese; //constructor屬性時指向它的構造函數  
var chin1 = new Chinese("小明"); 
chin1 instanceof Chinese; //true  
chin1 instanceof Human; //true 
function Human(){   //定義Human類
 this.species = "Human";
}
function Chinese(name){
 this.name = name;
}
Chinese.prototype = new Human(); //原型對象指向Human類
Chinese.prototype.constructor = Chinese; //constructor屬性時指向它的構造函數
var chin1 = new Chinese("小明");
chin1 instanceof Chinese; //true
chin1 instanceof Human; //true
這樣就實現瞭真正意義上的繼承.

相比對象冒充的方法,這樣的寫法不夠直觀.

但同時也解決瞭重復生成函數的問題.

 

最後,把原型繼承實現簡單的封裝:

[javascript]
Object.prototype.extendTo = function(parent) { 
    this.prototype = new parent(); 
    this.prototype.constructor = this; 
    this.uber = parent.prototype; 

 
function Human(){       //定義Human類  
    this.species = "Human"; 
    this.fun = function() { 
        return 0; 
    }; 

 
function Chinese(name){ 
    this.name = name; 

 
Chinese.extendTo(Human); //實現繼承.  
 
var chin1 = new Chinese("小明"); 
Object.prototype.extendTo = function(parent) {
 this.prototype = new parent();
 this.prototype.constructor = this;
 this.uber = parent.prototype;
}

function Human(){   //定義Human類
 this.species = "Human";
 this.fun = function() {
  return 0;
 };
}

function Chinese(name){
 this.name = name;
}

Chinese.extendTo(Human); //實現繼承.

var chin1 = new Chinese("小明");

 摘自 簡生的代碼備忘錄
 

發佈留言