js中處處是對象,物件導向的第一步當然就是封裝了,由於Js中沒有類的概念,所以封裝起來也比較麻煩,下面介紹兩種js的封裝。
1、使用約定優先的原則,將所有的私人變數以_開頭
<script type=text/javascript> /** * 使用約定優先的原則,把所有的私人變數都使用_開頭 */ var Person = function (no, name, age) { this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, checkNo: function (no) { if (!no.constructor == string || no.length != 4) throw new Error(學號必須為4位); }, setNo: function (no) { this.checkNo(no); this._no = no; }, getNo: function () { return this._no; }, setName: function (name) { this._name = name; }, getName: function () { return this._name; }, setAge: function (age) { this._age = age; }, getAge: function () { return this._age; }, toString: function () { return no = + this._no + , name = + this._name + , age = + this._age; } }; var p1 = new Person(0001, 鴻洋, 22); console.log(p1.toString()); //no = 0001 , name = 鴻洋 , age = 22 p1.setNo(0003); console.log(p1.toString()); //no = 0003 , name = 鴻洋 , age = 22 p1.no = 0004; p1._no = 0004; console.log(p1.toString()); //no = 0004 , name = 鴻洋 , age = 22 </script>
看完代碼,是不是有種被坑的感覺,僅僅把所有的變數以_開頭,其實還是可以直接存取的,這能叫封裝麼,當然了,說了是約定優先嘛,這種方式還是不錯的,最起碼成員變數的getter,setter方法都是prototype中,並非存在對象中,總體來說還是個不錯的選擇。如果你覺得,這不行,必須嚴格實現封裝,那麼看第二種方式。
2、嚴格實現封裝
<script type=text/javascript> /** * 使用這種方式雖然可以嚴格實現封裝,但是帶來的問題是get和set方法都不能儲存在prototype中,都是儲存在對象中的 * 這樣無形中就增加了開銷 */ var Person = function (no, name, age) { var _no , _name, _age ; var checkNo = function (no) { if (!no.constructor == string || no.length != 4) throw new Error(學號必須為4位); }; this.setNo = function (no) { checkNo(no); _no = no; }; this.getNo = function () { return _no; } this.setName = function (name) { _name = name; } this.getName = function () { return _name; } this.setAge = function (age) { _age = age; } this. getAge = function () { return _age; } this.setNo(no); this.setName(name); this.setAge(age); } Person.prototype = { constructor: Person, toString: function () { return no = + this.getNo() + , name = + this.getName() + , age = + this.getAge(); } } ; var p1 = new Person(0001, 鴻洋, 22); console.log(p1.toString()); //no = 0001 , name = 鴻洋 , age = 22 p1.setNo(0003); console.log(p1.toString()); //no = 0003 , name = 鴻洋 , age = 22 p1.no = 0004; console.log(p1.toString()); //no = 0003 , name = 鴻洋 , age = 22 </script>
看上面的代碼,去掉了this.屬性名稱,嚴格的實現了封裝,只能通過getter,setter訪問成員變數了,但是存在一個問題,所有的方法都存在對象中,增加了記憶體的開銷。
3、以閉包的方式封裝
<script type=text/javascript> /** * 使用這種方式雖然可以嚴格實現封裝,但是帶來的問題是get和set方法都不能儲存在prototype中,都是儲存在對象中的 * 這樣無形中就增加了開銷 */ var Person = (function () { var checkNo = function (no) { if (!no.constructor == string || no.length != 4) throw new Error(學號必須為4位); }; //共用變數 var times = 0; return function (no, name, age) { console.log(times++); // 0 ,1 , 2 var no , name , age; this.setNo = function (no) { checkNo(no); this._no = no; }; this.getNo = function () { return this._no; } this.setName = function (name) { this._name = name; } this.getName = function () { return this._name; } this.setAge = function (age) { this._age = age; } this. getAge = function () { return this._age; } this.setNo(no); this.setName(name); this.setAge(age); } })(); Person.prototype = { constructor: Person, toString: function () { return no = + this._no + , name = + this._name + , age = + this._age; } } ; var p1 = new Person(0001, 鴻洋, 22); var p2 = new Person(0002, abc, 23); var p3 = new Person(0003, aobama, 24); console.log(p1.toString()); //no = 0001 , name = 鴻洋 , age = 22 console.log(p2.toString()); //no = 0002 , name = abc , age = 23 console.log(p3.toString()); //no = 0003 , name = aobama , age = 24 </script>
上述代碼,js引擎載入完後,會直接執行Student = 立即執行函數,然後此函數返回了一個子函數,這個子函數才是new Student所調用的建構函式,又因為子函數中保持了對立即執行函數中checkNo(no) ,times的引用,(很明顯的閉包)所以對於checkNo和times,是所有Student對象所共有的,建立3個對象後,times分別為0,1,2 。這種方式的好處是,可以使Student中需要複用的方法和屬性做到私人且對象間共用。