什麼是面向對象?面向對象是一種思想!(廢話)。設計模式
面向對象能夠把程序中的關鍵模塊都視爲對象,而模塊擁有屬性及方法。這樣咱們若是把一些屬性及方法封裝起來,往後使用將很是方便,也能夠避免繁瑣重複的工做。接下來將爲你們講解在JS中面向對象的實現。函數
工廠模式測試
工廠模式是軟件工程領域一種廣爲人知的設計模式,而因爲在ECMAScript中沒法建立類,所以用函數封裝以特定接口建立對象。其實現方法很是簡單,也就是在函數內建立一個對象,給對象賦予屬性及方法再將對象返回便可。this
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/');
能夠看到工廠模式的實現方法很是簡單,解決了建立多個類似對象的問題,可是工廠模式卻無從識別對象的類型,由於所有都是Object,不像Date、Array等,所以出現了構造函數模式。url
構造函數模式spa
ECMAScript中構造函數能夠建立特定類型的對象,相似於Array、Date等原生JS的對象。其實現方法以下:prototype
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的實例,即解決了工廠模式中不能
這個例子與工廠模式中除了函數名不一樣之外,細心的童鞋應該發現許多不一樣之處:設計
構造函數雖然好用,但也並不是沒有缺點,使用構造函數的最大的問題在於每次建立實例的時候都要從新建立一次方法(理論上每次建立對象的時候對象的屬性均不一樣,而對象的方法是相同的),然而建立兩次徹底相同的方法是沒有必要的,所以,咱們能夠將函數移到對象外面(也許有些童鞋已經看出缺點,噓!)。指針
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設置成全局函數,這樣一來blog與blog2訪問的都是同一個函數,但是問題又來了,在全局做用域中定義了一個實際只想讓Blog使用的函數,顯示讓全局做用域有些名副其實,更讓人沒法接受的是在全局做用域中定義了許多僅供特定對象使用的方法,浪費空間不說,顯然失去了面向對象封裝性了,所以能夠經過原型來解決此問題。code
原型模式
咱們建立的每一個函數都有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(); // wyc1http://***.comfr1,fr2,fr3 blog2.alertInfo(); // wyc2http://+++.comfr1,fr2,fr3
原型模式也不是沒有缺點,首先,它省略了構造函數傳遞初始化參數這一環節,結果全部實例在默認狀況下都取得了相同的屬性值,這樣很是不方便,但這仍是不是原型的最大問題,原型模式的最大問題在於共享的本性所致使的,因爲共享,所以所以一個實例修改了引用,另外一個也隨之更改了引用。所以咱們一般不單獨使用原型,而是結合原型模式與構造函數模式。
混合模式(原型模式 + 構造函數模式)
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
混合模式中構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享屬性。每一個實例都會有本身的一份實例屬性,但同時又共享着方法,最大限度的節省了內存。另外這種模式還支持傳遞初始參數。優勢甚多。這種模式在ECMAScript中是使用最普遍、認同度最高的一種建立自定義對象的方法。
動態原型模式
動態原型模式將全部信息封裝在了構造函數中,而經過構造函數中初始化原型(僅第一個對象實例化時初始化原型),這個能夠經過判斷該方法是否有效而選擇是否須要初始化原型。
function Blog(name, url) { this.name = name; this.url = url; if (typeof this.alertInfo != 'function') { // 這段代碼只執行了一次 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',即當blog初始化時,這樣作blog2就不在須要初始化原型,對於使用這種模式建立對象,能夠算是perfect了。
此博文參考《JavaScript高級程序設計》第3版,但語言都通過簡化,例子也重寫過,若是有什麼不懂的地方請留言回覆,做者將更新博客。