對於熟悉C#和Java的兄弟們,物件導向的三大思想(封裝,繼承,多態)肯定是瞭解的,今天我想講講如何在Javascript中利用封裝這個特性,開講!
我們會把現實中的一些事物抽象成一個Class並且把事物的屬性(名詞)作為Class的Property把事物的動作(動詞)作為Class的methods。在物件導向的語言中(C#等)都會有一些關鍵字來修飾類或者屬性(Private,public,protect),這些關鍵詞描述了訪問的許可權,不多做解釋。
我們來看看Javascript的易變的特性(我們還用上一次的例子):
var Man = function (name, age) { this.Name = name; this.Age = age; } var Person = new Interface("Person", ["GetName", "GetAge"]); Man.prototype = { GetName: function () { return this.Name; }, GetAge: function () { return this.Age; } } var Alan = new Man("Alan", 25); alert(Alan.GetAge()); Alan.DisplayAll = function () { return "Name: "+this.GetName() + "; Age: " + this.GetAge() } alert(Alan.DisplayAll());
我先建立了一個Class(Javascript的匿名方法)擁有2個公用的(public)的欄位(本篇blog會詳細講解,繼續往下看)和2個public的方法,我們建立了一個Instance--Alan,但是我可以為這個Instance動態添加一個DisplayAll的方法,我想任何物件導向的語言是做不到這一點的,Javascript的靈活體現之一。
我們現在假設一個情境,如果有很多的程式員要用這段代碼,由於Javascript的易變性,程式員就可以在執行個體化後改變Name的值,那初始化的動作就沒有意義了:
var Alan = new Man("Alan", 25); Alan.Name = "Alice"; //悲劇了,我alert的時候變成Alice了 alert(Alan.GetName());
所以我們不能讓外部的人去任意的修改這個欄位,在Java或C#中我們只需要個這個欄位改為Private,就萬事OK了,但是Javascript沒有這個關鍵詞,那我們需要這麼做呢,這就是這篇blog存在的意義
我們可以想下在C#除了設定Private之外我們還可以怎麼做?我們可以設定Setter和Getter方法。
我們來修改下上面的代碼:我們稱方法一:
var Person = new Interface("Person", ["SetName", "SetAge", "GetName", "GetAge"]); var Man = function (name, age) { this.SetAge(age); this.SetName(name); } Man.prototype = { SetName: function (name) { this.Name = name; }, SetAge: function (age) { this.Age = age; }, GetName: function () { return this.Name; }, GetAge: function () { return this.Age; } } var Alan = new Man("Alan", 25); Alan.Name = "Alice"; //悲劇了,我alert的時候變成Alice了 Alan.SetAge(10);//悲劇,被別人把我的年齡給這麼小 alert(Alan.GetName()); Alan.DisplayAll = function () { return "Name: "+this.GetName() + "; Age: " + this.GetAge() } alert(Alan.DisplayAll());
我們發現貌似樣子很像C#中的Setter和Getter,但是還是可以被外部修改。但是從約束上來看,貌似比上面的code要好看些,通過方法來設定初始值。但是問題還是沒有解決,我們來看看下面一種方法:閉包
//我需要解釋一下,在Javascript中是通過This關鍵字來開發許可權的(Public)。
在講閉包之前,我們需要瞭解下閉包的本質: 在Javascript中,只有方法是有範圍的,如果在方法中聲明的變數在外部是無法訪問的,那Private的概念就出來了。
var Person = new Interface("Person", ["SetName", "SetAge", "GetName", "GetAge"]); var Man = function (newname, newage) { var name, age; this.SetName = function (newname) { name = newname; } this.SetAge = function (newage) { age = newage; } this.GetName = function () { return name; } this.GetAge = function () { return age; } this.SetAge(newage); this.SetName(newname); } var Alan = new Man("Alan", 25); Alan.name="Alice"; //現在name是private了,我是無法去修改的 Alan.SetAge(10); //悲劇,被別人把我的年齡給這麼小 alert(Alan.GetAge());
現在私人的功能就實現了,我們只是用Var來代替了This而已。//我們把公用(Public)並且可以訪問Private的方法稱為特權方法,比如上面的this.SetName, this.SetAge.
如果我們的公用方法不涉及到訪問Private的欄位,那我們可以把他們放到Prototype中。//好處是多個執行個體的時候記憶體中也只有一分拷貝
Man.prototype.DisplayAll = function () { return "Name: " + this.GetName() + "; Age: " + this.GetAge() }
哈哈~我們來看下稍微有點難度的東西:靜態變數和方法
我們都是知道靜態東西屬於類(Class),我們來修改下上面的代碼:
var Person = new Interface("Person", ["SetName", "SetAge", "GetName", "GetAge","GetCount"]); var Man = (function () { var count = 0; return function (newname, newage) { var name, age; this.SetName = function (newname) { name = newname; } this.SetAge = function (newage) { age = newage; } this.GetName = function () { return name; } this.GetAge = function () { return age; } this.GetCount = function () { return count; } this.SetAge(newage); this.SetName(newname); count++; } })(); Man.prototype.DisplayAll = function () { return "Name: " + this.GetName() + "; Age: " + this.GetAge() } var Alan1 = new Man("Alan", 25); var Alan2 = new Man("Alan", 25); alert("There are "+Alan2.GetCount()+" instances of Man" );