1. 程式人生 > >理解繼承(一):JS面向物件程式設計(封裝)

理解繼承(一):JS面向物件程式設計(封裝)

依然是向阮前輩(阮一峰)學習的部落格原文
學了這一階段和峰大神的幾篇文章,打算下一階段滾回去把指標好好學一下
JS 中,萬物皆物件,但是它又不是一門真正的面向物件程式語言。
那如果我們要把“屬性(prototype)”和方法(“method”)封裝成一個物件,甚至要從原型物件生成一個例項物件,我們應該怎麼做?

一:原始生成例項物件的模式

var Cat = {
 name:'',
 color:''
}

我們需要根據這個原型物件的規格生成兩個例項物件

var cat1 = {};//建立一個空物件
cat1.name = "田園犬";
cat1.color = "紅色"
;//按照原型物件的屬性賦值 var cat2 = {}; cat2.name = "泰迪"; cat2.color = "黃色";

上面是最簡單的封裝,把兩個屬性封裝在一個物件裡面。但是,這樣的寫法有兩個缺點,一是如果多生成幾個例項,寫起來就非常麻煩,二是例項與原型之間沒有任何辦法產生什麼聯絡

二:原始模式的改進

我們可以寫一個函式來解決程式碼 重複的問題:

function Cat(name,color){
  return{
  name:name,
  color:color
}
}

然後在例項物件的時候,直接呼叫函式:

var cat1 = Cat("泰迪","黃色"
); var cat2 = Cat("田園犬","紅色");

但這種方法依然存在問題,cat1和cat2之間沒有內在來呢西,不能反映出他們是同一個原型物件的例項

三:建構函式模式

為了解決從原型物件生成例項的問題,Javascript提供了一個建構函式(Constructor)模式。
所謂的建構函式其實就是普通函式,但是其內部使用了this變數,對建構函式使用new運算子就能生成例項,並且this變數會繫結在例項物件上。
比如:貓的原型物件可以這樣寫:

function Cat(name,color){
  this.name = name;
  this.color = color;
}

現在就可以生成例項物件了:

var cat1 = new Cat("泰迪","黃色");
var cat2 = new Cat("田園犬","黑色");
alert(cat1.name);//泰迪
alert(cat1.color);//黃色

這時cat1和cat2會自動含有一個constructor屬性,指向建構函式。

alert(cat1.constructor == Cat);//true
alert(cat2.constructor == Cat);//true

JS 提供了一個instanceof運算子,驗證原型物件與例項物件之間的聯絡

alert(cat1 instanceof Cat);//true
alert(cat2 instanceof Cat);//true

四.建構函式模式問題

建構函式的方法很好用,但是存在一個記憶體浪費的問題
例如,我們現在為Cat物件新增一個不變的屬性type種類,再新增一個方法eat(吃)。那麼,原型物件Cat就變成了下面這個樣子:

 function Cat(name,color){
  this.name = name;
  this.color = color;
  this.type = "貓科動物";
  this.eat = function(){alert("吃老鼠");};
}

採用同樣的方法生成例項:

var cat1  = new Cat("泰迪","黃色");
var cat2 = new Cat("田園犬","紅色");
alert (cat1.type);//貓科動物
cat1.eat();//吃老鼠

表面上看這樣好像並沒有什麼問題,但是實際上存在很大的弊端。那就是對於每一個例項物件,type屬性和eat()方法都是一摸一樣的內容,每一次生成一個例項,都必須為重複內容,多佔用一些記憶體,這樣既不環保,也缺乏效率。

console.log(cat1.eat == cat2.eat);//false

可不可以讓type屬性和eat()方法在記憶體中只生成一次,然後所有例項都指向那個記憶體地址?回答是可以的

五.Prototype模式

Javascript規定,每一個建構函式都有一個prototype屬性,指向另一個物件。這個物件的所有屬性和方法,都會被建構函式的例項繼承。
這意味著,我們可以把那些不變的屬性和方法,直接定義在prototype物件上。

function Cat(name,color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "貓科動物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

然後生成例項:

var cat1 = new Cat("大毛","黃色");
  var cat2 = new Cat("二毛","黑色");
  alert(cat1.type); // 貓科動物
  cat1.eat(); // 吃老鼠

這時,所有例項的type屬性和eat()方法,其實都是同一個記憶體地址,指向prototype物件,因此就提高了執行效率

 alert(cat1.eat == cat2.eat); //true

六.Prototype模式的驗證方法

為了配合prototype屬性,Javascript定義了一些輔助方法,幫助我們使用它

6.1isPrototypeOf()

用著方法來判斷,某個prototype和某個例項之間的關係。

console.log(Cat.prototype.isPrototypeOf(cat1));//true
console.log(Cat.prototype.isPrototypeOf(cat2));//true

6.2hasOwnPrototype()

每個例項物件都有一個hasOwnPrototype()方法,用來判斷某個屬性到底是本地屬性(自由屬性)還是 繼承屬性

alert(cat1.hasOwnPrototype("name"));//true
alert(cat2.hasOwnPrototype("name"));//true

6.3in運算子

in運算子可以用來判斷,某個例項是否含有某個屬性,如果物件的自有屬性或者繼承屬性中包含這個屬性就返回true


for(var prop in cat1){alert("cat1["+prop+"]="+cat1[prop]);}
·····