面向?qū)ο罂梢园殉绦蛑械年P(guān)鍵模塊都視為對象,而模塊擁有屬性及方法。這樣我們?nèi)绻岩恍傩约胺椒ǚ庋b起來,日后使用將非常方便,也可以避免繁瑣重復(fù)的工作。接下來將為大家講解在JS中面向?qū)ο蟮膶崿F(xiàn)。
一.工廠模式
工廠模式是軟件工程領(lǐng)域一種廣為人知的設(shè)計模式,而由于在ECMAScript中無法創(chuàng)建類,因此用函數(shù)封裝以特定接口創(chuàng)建對象。其實現(xiàn)方法非常簡單,也就是在函數(shù)內(nèi)創(chuàng)建一個對象,給對象賦予屬性及方法再將對象返回即可。
function createBlog(name, url) {
var o = new Object();
o.name = name;
o.url = url;
o.sayUrl= function() {
alert(this.url);
}
return o;
}
var blog1 = createBlog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/');
- 可以看到工廠模式的實現(xiàn)方法非常簡單,解決了創(chuàng)建多個相似對象的問題,但是工廠模式卻無從識別對象的類型,因為全部都是Object,不像Date、Array等,因此出現(xiàn)了構(gòu)造函數(shù)模式。
二.構(gòu)造函數(shù)模式
ECMAScript中構(gòu)造函數(shù)可以創(chuàng)建特定類型的對象,類似于Array、Date等原生JS的對象。其實現(xiàn)方法如下:
function Blog(name, url) {
this.name = name;
this.url = url;
this.alertUrl = function() {
alert(this.url);
}
}
var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/');
console.log(blog instanceof Blog); // true, 判斷blog是否是Blog的實例,即解決了工廠模式中不能
-
這個例子與工廠模式中除了函數(shù)名不同以外,細心的童鞋應(yīng)該發(fā)現(xiàn)許多不同之處:
- 函數(shù)名首寫字母為大寫 ?。m然標準沒有嚴格規(guī)定首寫字母為大寫,但按照慣例,構(gòu)造函數(shù)的首寫字母用大寫
- 沒有顯示的創(chuàng)建對象
- 直接將屬性和方法賦值給了this對象
- 沒有return語句
- 使用new創(chuàng)建對象
- 能夠識別對象(這正是構(gòu)造函數(shù)模式勝于工廠模式的地方)
構(gòu)造函數(shù)雖然好用,但也并非沒有缺點,使用構(gòu)造函數(shù)的最大的問題在于每次創(chuàng)建實例的時候都要重新創(chuàng)建一次方法(理論上每次創(chuàng)建對象的時候?qū)ο蟮膶傩跃煌?,而對象的方法是相同的),然而?chuàng)建兩次完全相同的方法是沒有必要的,因此,我們可以將函數(shù)移到對象外面(也許有些童鞋已經(jīng)看出缺點,噓!)。
function Blog(name, url) {
this.name = name;
this.url = url;
this.alertUrl = alertUrl;
}
function alertUrl() {
alert(this.url);
}
var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/'),
blog2 = new Blog('cnblogs', 'http://www.cnblogs.com/');
blog.alertUrl(); // http://www.cnblogs.com/wuyuchang/
blog2.alertUrl(); // http://www.cnblogs.com/
- 我們將alertUrl設(shè)置成全局函數(shù),這樣一來blog與blog2訪問的都是同一個函數(shù),可是問題又來了,在全局作用域中定義了一個實際只想讓Blog使用的函數(shù),顯示讓全局作用域有些名副其實,更讓人無法接受的是在全局作用域中定義了許多僅供特定對象使用的方法,浪費空間不說,顯然失去了面向?qū)ο蠓庋b性了,因此可以通過原型來解決此問題。
三.原型
我們創(chuàng)建的每個函數(shù)都有prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。使用原型對象的好處就是可以讓所有對象實例共享它所包含的屬性及方法。
function Blog() {
}
Blog.prototype.name = 'wuyuchang';
Blog.prototype.url = 'http://www.cnblogs.com/wuyuchang/';
Blog.prototype.friend = ['fr1', 'fr2', 'fr3', 'fr4'];
Blog.prototype.alertInfo = function() {
alert(this.name + this.url + this.friend );
}
// 以下為測試代碼
var blog = new Blog(),
blog2 = new Blog();
blog.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2,fr3,fr4
blog2.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2,fr3,fr4
blog.name = 'wyc1';
blog.url = 'http://***.com';
blog.friend.pop();
blog2.name = 'wyc2';
blog2.url = 'http://+++.com';
blog.alertInfo(); // wyc1 http://***.comfr1,fr2,fr3
blog2.alertInfo(); // wyc2 http://+++.comfr1,fr2,fr3
原型模式也不是沒有缺點,首先,它省略了構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié),結(jié)果所有實例在默認情況下都取得了相同的屬性值,這樣非常不方便,但這還是不是原型的最大問題,原型模式的最大問題在于共享的本性所導(dǎo)致的,由于共享,因此因此一個實例修改了引用,另一個也隨之更改了引用。因此我們通常不單獨使用原型,而是結(jié)合原型模式與構(gòu)造函數(shù)模式。
四.混合模式(原型模式 + 構(gòu)造函數(shù)模式)
function Blog(name, url, friend) {
this.name = name;
this.url = url;
this.friend = friend;
}
Blog.prototype.alertInfo = function() {
alert(this.name + this.url + this.friend);
}
var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/', ['fr1', 'fr2', 'fr3']),
blog2 = new Blog('wyc', 'http://**.com', ['a', 'b']);
blog.friend.pop();
blog.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2
blog2.alertInfo(); // wychttp://**.coma,b
混合模式中構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享屬性。每個實例都會有自己的一份實例屬性,但同時又共享著方法,最大限度的節(jié)省了內(nèi)存。另外這種模式還支持傳遞初始參數(shù)。優(yōu)點甚多。這種模式在ECMAScript中是使用最廣泛、認同度最高的一種創(chuàng)建自定義對象的方法。
五.動態(tài)原型模式
動態(tài)原型模式將所有信息封裝在了構(gòu)造函數(shù)中,而通過構(gòu)造函數(shù)中初始化原型(僅第一個對象實例化時初始化原型),這個可以通過判斷該方法是否有效而選擇是否需要初始化原型。
function Blog(name, url) {
this.name = name;
this.url = url;
if (typeof this.alertInfo != 'function') {
// 這段代碼只執(zhí)行了一次
alert('exe time');
Blog.prototype.alertInfo = function() {
alert(thia.name + this.url);
}
}
}
var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang'),
blog2 = new Blog('wyc', 'http:***.com');
可以看到上面的例子中只彈出一次窗,'exe time',即當(dāng)blog初始化時,這樣做blog2就不在需要初始化原型,對于使用這種模式創(chuàng)建對象,可以算是perfect了。