js類的基本含義
我們知道,在js中,是沒有類的概念的。類的所有執行個體對象都從同一個原型對象上繼承屬性,因此,原型對象是類的核心。
類是對象的抽象,而對象是類的具體執行個體。類是抽象的,不佔用記憶體,而對象是具體的,佔用儲存空間。———百度百科
早期的javascript需求都很簡單,基本都是寫成函數的,然後是面向過程的寫法,後來慢慢的引入物件導向開發思想,再後來就慢慢寫成 類。
在js中,寫成類的本質基本都是 建構函式+原型。下面,就討論一下js類的幾種寫法:
建構函式 法
/**
* Person類:定義一個人,有name屬性和getName方法
*/ <script>
function Person(name){
this.name = name;
this.getName = function(){
return this.name;
}
} //我們在這裡執行個體化幾個對象
var p1 = new Person("trigkit4");
var p2 = new Person("mike");
console.log(p1 instanceof Person);//true
console.log(p2 instanceof Person);//true
</script>
由上面控制台輸出結果可知,p1和p2的確是類Person的執行個體對象。instanceof操作符左邊是待檢測類的對象,右邊是定義類的建構函式。這裡,instanceof用來檢測對象p1是否屬於Person類。
這種方式的優點是:我們可以根據參數來構造不同的對象執行個體 ,缺點是每次構造執行個體對象時都會產生getName方法,造成了記憶體的浪費 。
我們可以用一個外部函數來代替類方法,達到了每個對象共用同一個方法。改寫後的類如下:
//外部函數<script>
function getName() {
return this.name;
} function Person(name)
{ this.name = name;
this.getName = getName;//
}</script>
原型方式
<script>
function Person(){};
Person.prototype.name = "trigkit4";//類的屬性都放在prototype上
Person.prototype.getName = function(){ return " I'm " + this.name;
} var p1 = new Person(); var p2 = new Person(); console.log(p1.name);//trigkit4
console.log(p2.getName());//I'm trigkit4</script>
原型方式的缺點就是不能通過參數來構造對象執行個體 (一般每個對象的屬性是不相同的) ,優點是所有對象執行個體都共用getName方法(相對於建構函式方式),沒有造成記憶體浪費 。
建構函式+原型方式
取前面兩種的優點:
a、用建構函式來定義類屬性(欄位)。
b、用原型方式來定義類的方法。
<script>
function Person(name){ this.name = name;
} //原型的特性可以讓對象執行個體共用getName方法
Person.prototype.getName = function(){ return " I'm " + this.name;
}</script>
這樣,我們就既可以構造不同屬性的對象,也可以讓對象執行個體共用方法,不會造成記憶體的浪費。
為了讓js代碼風格更緊湊,我們讓prototype方法代碼移到function Person的大括弧內。
<script>
function Person(name){
this.name = name;
Person.prototype.getName = function(){
return this.name;
}
} var p1 = new Person('trigkit4');
console.log(p1.getName());//trigkit4
</script>
補充:
首先說說類,在一個類裡我們會有以下的幾個特徵:
1. 公有方法
2. 私人方法
3. 屬性
4. 私人變數
5. 解構函式
一、建構函式法
這是經典方法,也是教科書必教的方法。它用建構函式類比"類",在其內部用this關鍵字指代執行個體對象。
function Cat() {
this.name = "大毛";
}
產生執行個體的時候,使用new關鍵字。
var cat1 = new Cat();
alert(cat1.name); // 大毛
類的屬性和方法,還可以定義在建構函式的prototype對象之上。
Cat.prototype.makeSound = function(){
alert("喵喵喵");
}
關於這種方法的詳細介紹,請看我寫的系列文章《Javascript 物件導向編程》,這裡就不多說了。它的主要缺點是,比較複雜,用到了this和prototype,編寫和閱讀都很費力。
二、Object.create()法
為瞭解決"建構函式法"的缺點,更方便地產生對象,Javascript的國際標準ECMAScript第五版(目前通行的是第三版),提出了一個新的方法Object.create()。
用這個方法,"類"就是一個對象,不是函數。
var Cat = {
name: "大毛",
makeSound: function(){ alert("喵喵喵"); }
};
然後,直接用Object.create()產生執行個體,不需要用到new。
var cat1 = Object.create(Cat);
alert(cat1.name); // 大毛
cat1.makeSound(); // 喵喵喵
目前,各大瀏覽器的最新版本(包括IE9)都部署了這個方法。如果遇到老式瀏覽器,可以用下面的代碼自行部署。
if (!Object.create) {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
這種方法比"建構函式法"簡單,但是不能實現私人屬性和私人方法,執行個體對象之間也不能共用資料,對"類"的類比不夠全面。
三、極簡主義法
荷蘭程式員Gabor de Mooij提出了一種比Object.create()更好的新方法,他稱這種方法為"極簡主義法"(minimalist approach)。這也是我推薦的方法。
3.1 封裝
這種方法不使用this和prototype,代碼部署起來非常簡單,這大概也是它被叫做"極簡主義法"的原因。
首先,它也是用一個對象類比"類"。在這個類裡面,定義一個建構函式createNew(),用來產生執行個體。
var Cat = {
createNew: function(){
// some code here
}
};
然後,在createNew()裡面,定義一個執行個體對象,把這個執行個體對象作為傳回值。
var Cat = {
createNew: function(){
var cat = {};
cat.name = "大毛";
cat.makeSound = function(){ alert("喵喵喵"); };
return cat;
}
};
使用的時候,調用createNew()方法,就可以得到執行個體對象。
var cat1 = Cat.createNew();
cat1.makeSound(); // 喵喵喵
這種方法的好處是,容易理解,結構清晰優雅,符合傳統的"物件導向編程"的構造,因此可以方便地部署下面的特性。
3.2 繼承
讓一個類繼承另一個類,實現起來很方便。只要在前者的createNew()方法中,調用後者的createNew()方法即可。
先定義一個Animal類。
var Animal = {
createNew: function(){
var animal = {};
animal.sleep = function(){ alert("睡懶覺"); };
return animal;
}
};
然後,在Cat的createNew()方法中,調用Animal的createNew()方法。
var Cat = {
createNew: function(){
var cat = Animal.createNew();
cat.name = "大毛";
cat.makeSound = function(){ alert("喵喵喵"); };
return cat;
}
};
這樣得到的Cat執行個體,就會同時繼承Cat類和Animal類。
var cat1 = Cat.createNew();
cat1.sleep(); // 睡懶覺
3.3 私人屬性和私人方法
在createNew()方法中,只要不是定義在cat對象上的方法和屬性,都是私人的。
var Cat = {
createNew: function(){
var cat = {};
var sound = "喵喵喵";
cat.makeSound = function(){ alert(sound); };
return cat;
}
};
上例的內部變數sound,外部無法讀取,只有通過cat的公有方法makeSound()來讀取。
var cat1 = Cat.createNew();
alert(cat1.sound); // undefined
3.4 資料共用
有時候,我們需要所有執行個體對象,能夠讀寫同一項內部資料。這個時候,只要把這個內部資料,封裝在類對象的裡面、createNew()方法的外面即可。
var Cat = {
sound : "喵喵喵",
createNew: function(){
var cat = {};
cat.makeSound = function(){ alert(Cat.sound); };
cat.changeSound = function(x){ Cat.sound = x; };
return cat;
}
};
然後,產生兩個執行個體對象:
var cat1 = Cat.createNew();
var cat2 = Cat.createNew();
cat1.makeSound(); // 喵喵喵
這時,如果有一個執行個體對象,修改了共用的資料,另一個執行個體對象也會受到影響。
cat2.changeSound("啦啦啦");
cat1.makeSound(); // 啦啦啦