JavaScript 建立物件和繼承
首先宣告下,本人不擅長文字表達,文采不行,所以文章中文字較少,請看程式碼
建立物件
JS中可以有許多設計模式,在這些中推薦使用組合建構函式和原型模式;
因為不太善於寫文章,所以下面簡化文字敘述,直接看程式碼
工廠模式
function person(name,age){
var obj={}
obj.name=name;
obj.age=age;
obj.sayName=function(){
console. log(this.name);
}
return obj;
}
建構函式模式
正常情況下我們將函式名起為大寫字母開頭
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
console.log(this.name);
}
}
var a=new Person("張三",29);
var b=new Person("李四",22);
a.sayName();
b.sayName();
建構函式可以通過 new
原型模式
function Person(){
}
Person.prototype.name = "Li Si";
Person.prototype.age = 21;
Person.prototype.sayName=function(){
console.log(this.name);
}
var person1=new Person();
person1.sayName(); //"Li Si"
var person2=new Person();
person2.sayName(); //"Li Si"
原型模式所有的都共用同一個資料,相當於公有變數
組合模式:建構函式和原型模式結合
通過上面兩個例子,兩個都有獨有的特性,所以我們可以組合兩者了來進行處理,一般推薦使用組合模式
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayName=function(){
console.log(this.name);
}
var person1=new Person("張三");
person1.sayName(); //"張三"
var person2=new Person("李四");
person2.sayName(); //"李四"
這樣各自經過例項化後,都有自己的獨有資料,但是卻有著公共方法;
繼承
ES5 繼承有許多中方式,我們這裡只說普通常用的繼承方式
原型鏈賦值繼承
function father(name){
this.name=name;
}
father.prototype.sayName=function(){
console.log(this.name);
}
function sub(age){
this.age=age;
}
sub.prototype=new father();
sub.prototype.sayAge=function(){
console.log(this.age);
}
上面就是典型的原型鏈賦值繼承,但是這個繼承是有缺點的,在繼承時需要 new
關鍵字進行處理。
建構函式繼承
function father(name){
this.name=name;
this.age=22;
}
father.prototype.sayName=function(){
console.log(this.name);
}
function sub(){
father.apply(this,arguments)
}
var s=new sub("a")
> s
> sub {name: "a", age: 22}
age: 22
name: "a"
__proto__:
constructor: ƒ sub()
__proto__: Object
建構函式繼承最後在輸出中會發現並沒有 父級的方法 ,但是可以將資料傳到父級,與原型鏈繼承互補,所以衍生出組合繼承的方式
組合繼承
function Father(name) {
this.name = name;
this.className = "Father"
}
Father.prototype.sayName = function () {
console.log(this.name)
}
function Sub(name) {
Father.apply(this, arguments)
}
//繼承原型
Sub.prototype = new Father();
var s=new Sub("張三",12);
不僅會繼承建構函式中的屬性,也會複製父類原型鏈中的屬性
但是在 Sub.prototype = new Father();
之後
Sub 的原型變成這樣的了
> Sub.prototype
> {name: undefined, className: "person"}
也就是說Sub的原型中已經有了一個name屬性,而之後建立 s 時傳給構造的函式的name則是通過this重新定義了一個name屬性,相當於只是覆蓋掉了原型的name屬性(原型中的name依然還在),這樣很不優雅。
寄生組合繼承
function Father(name) {
this.name = name;
this.className = "Father"
}
Father.prototype.sayName = function () {
console.log(this.name)
}
function Sub(name,age) {
this.age=age;
Father.call(this, name)
}
// 注意此處
Sub.prototype=Object.create(Father.prototype)
Sub.prototype.sayAge=function(){
console.log(this.age)
}
這裡用到了 Object.creat(obj)
方法,該方法會對傳入的obj物件進行淺拷貝。和上面組合繼承的主要區別就是:將父類的原型複製給了子類原型。這種做法很清晰:
- 建構函式中繼承父類屬性/方法,並初始化父類。
- 子類原型和父類原型建立聯絡。
還有一個問題,就是constructor屬性,我們來看一下:
> Father.prototype.constructor
< Father(name){
this.name=name;
this.className="Father"
}
> Sub.prototype.constructor
< Father(name){
this.name=name;
this.className="Father"
}
constructor是類的建構函式,我們發現,Father和Sub例項的constructor指向都是Father,當然,這並不會改變instanceof的結果,但是對於需要用到construcor的場景,就會有問題。所以一般我們會加上這麼一句:
Sub.prototype.constructor = Sub