JS物件繼承時,對引用屬性的處理

elvisxiao發表於2019-03-01

前陣子跟一個同事說:建立物件時,原型上只定義方法就行,屬性定義在建構函式裡面。他問了句:為什麼?我居然思索了半天,知識有時真的不用就會忘,通過寫作能加深印象,我們來看下面的例子:


function SuperCompany() {}

SuperCompany.prototype.staffs = [];

SuperCompany.prototype.addStaff = function(name) {
	this.staffs.push(name);
}

SuperCompany.prototype.printStaff = function() {
	console.log(`staffs:`, this.staffs);	
}


function Company() {}
Company.prototype = new SuperCompany();

let companyA = new Company();
companyA.addStaff(`peter`);

let companyB = new Company();
companyB.addStaff(`nina`);

companyA.printStaff();
companyB.printStaff();

複製程式碼

上述程式碼,輸出都是一樣的:[ `peter`, `nina` ],很明顯,兩個不同的子類之間的資料相互混雜在一起了。那我們試試將屬性移到建構函式裡面:


function SuperCompany() {
	this.staffs = [];
}

SuperCompany.prototype.addStaff = function(name) {
	this.staffs.push(name);
}

SuperCompany.prototype.printStaff = function() {
	console.log(`staffs:`, this.staffs);	
}


function Company() {}
Company.prototype = new SuperCompany();

let companyA = new Company();
companyA.addStaff(`peter`);

let companyB = new Company();
companyB.addStaff(`nina`);

companyA.printStaff();
companyB.printStaff();
複製程式碼

執行發現,結果並沒有變化,兩個不同的子類雖然複製了自己的staffs,但staffs是個引用型別,他們只複製了引用地址而已,指向的具體資料還是同一份,那麼怎麼解決呢?做下變通:


function SuperCompany() {
	this.staffs = [];
}

SuperCompany.prototype.addStaff = function(name) {
	this.staffs.push(name);
}

SuperCompany.prototype.printStaff = function() {
	console.log(`staffs:`, this.staffs);	
}


function Company() {
	SuperCompany.call(this);
}
Company.prototype = new SuperCompany();

let companyA = new Company();
companyA.addStaff(`peter`);

let companyB = new Company();
companyB.addStaff(`nina`);

companyA.printStaff();
companyB.printStaff();

複製程式碼

再看下輸出,[ `peter` ]、[ `nina` ],問題是不是就解決了?這種方法被稱作”借用建構函式“(有時也稱為偽造物件或者經典繼承)。

溫故而知新,有時東西工作時寫成了習慣,就會忘記原理。

相關文章