js的原型繼承 -- prototype
先說下什麼是prorotype?
- js中,俗話說“一切皆物件”。用
new
出來的都是函式物件;否則就是普通物件 - 函式物件都有
prototype
(原型物件);而普通物件則只有__proto__
(原型指標) - 函式物件的一個特點:可以實現不同類之間的方法繼承
- 函式的子類可以共享父類的方法,而父類不能想用子類的方法
eg: (prototype的繼承)
//建立父類函式物件
function Personal(name, age) {
this.name = name; //父類的私有屬性
this.age = age;
this.house = ['北京', '上海']
}
Personal.prototype.run = function() { //給父類原型動態新增方法
alert('原型方法:' + this.name + ' is running!');
}
var per = new Personal('小白', 24)
per.run() //列印 --> 原型方法:小白 is running!
//建立子類函式物件
function Boy() {}
Boy.prototype = new Personal('小黑', 19) //子類繼承父類的所有屬性和方法
Boy.prototype.source = 100 //給子類新增原型屬性
Boy.prototype.printSource = function() { //給子類新增方法
alert(this.name + '的原型方法printSouce列印成績為:' + this.source) //小黑的原型方法printSouce列印成績為:100
}
Boy.prototype.run() //列印 --> 原型方法:小黑 is running!
var boys = new Boy()
boys.printSource()
console.log(boys, '--boys---') //列印 -->19, 小黑, 100 (這裡會沿著prototype向上查詢到Personal的屬性)
複製程式碼
以下是關於prototype繼承需要注意的點:
- 如果父類中有引用型別的屬性:Array,Object等。子類繼承了這些屬性,並嘗試改變的話,會影響到父類的屬性。
//建立另外一個例項1:
var boys1 = new Boy()
boys1.house.push('深圳')
//列印這兩個例項:
console.log(boys, boys1)
複製程式碼
可以看出來,當屬性為引用型別時,只要有一個例項的屬性做了操作,所有的例項都會受到影響。
- 該方式導致 Boy.prototype.constructor 被重寫,它指向的是 Personal 而非 Boy。因此你需要手動將 Boy.prototype.constructor 指回 Boy。
Boy.prototype = new Personal();
Boy.prototype.constructor === Personal; // true
// 重寫 Boy.prototype 中的 constructor 屬性,指向自己的建構函式 Boy
Boy.prototype.constructor = Boy;
複製程式碼
- 因為 Boy.prototype = new Personal(); 重寫了 Boy 的原型物件,所以 printSource 放在重寫原型物件之前會被覆蓋掉,因此給子類新增原型方法必須在替換原型之後(eg是沒有被覆蓋的)。
function Boy() {}
Boy.prototype = new Personal();
// 給子類新增原型方法必須在替換原型之後
Boy.prototype.printSource = function() {
console.log('printSource~');
};
複製程式碼
- 建立 boys 例項時無法向父類的建構函式傳參,也就是無法初始化 source屬性。因此:只能建立例項之後再修改父類的屬性。
const boys = new Boy();
// 只能建立例項之後再修改父類的屬性
boys.source = 100;
複製程式碼
apply()、call()方法的繼承
瞭解下apply()、call()方法
- apply()、call()的用法:
obj.call(thisObj, arg1, arg2, ...);
obj.apply(thisObj, [arg1, arg2, ...]);
複製程式碼
obj是父級,thisObj是子級;第二個引數apply可以接收一個陣列,而call只能是每項逐個接收。
- apply和call 本來就是為了擴充套件函式的作用域而生的,換句話說就是為了改變this的指向存在的。
- 當一個object沒有某種方法,但是其他的有,我們可以藉助call和apply來用其他物件的方法來做操作,也可以傳引數。
//eg:
function Personal(name, sex) {
this.name = name;
this.sex = sex;
this.say = function (){
alert('姓名:' + this.name + ';性別:' + this.sex)
}
}
const per = new Personal('Allan', '男')
per.say();
//apply()方法實現:
function Girls(name, sex) {
Personal.apply(this, [name, sex]);
//Person.apply(this,arguments); //跟上句一樣的效果,arguments
//Print.apply(this,arguments); //還可以實現繼承多個父類,但是原型 prototype只能繼承一個父類!!!切記
}
const girls1 = new Girls('Lucy', '女')
girls1.say();
//call()實現:
function Boy(name, sex) {
Personal.call(this, name, sex);
}
const boys = new Boy('Barry', '男');
boys.say() //
複製程式碼
總結:
- prototype可以動態的給物件增加屬性和方法。
- 可以實現子類繼承父類,擁有父類的屬性和方法。
- call和apply的區別,在於引數的不同。
- call和apply,理解為在子類的執行環境中執行父類的方法和屬性。
- call和apply可以實現一個子類繼承多個父類,但是prototype只能有一個父類。
如果覺得對你有幫助,請給作者一點小小的鼓勵,點個贊或者收藏吧。
(有需要溝通的請聯絡我:微信( wx9456d ) 郵箱( allan_liu986@163.com )