javascript設(shè)計(jì)模式 封裝和信息隱藏(上)
更新時(shí)間:2012年07月24日 00:46:59 作者:
今天博文關(guān)注的是javascript中的封裝,文章內(nèi)容來自《pro javascript design patterns》(有興趣的朋友可以直接去下)和自己對(duì)這一問題的理解
本文分上下兩部分,上部講基本模式(basic patterns):完全暴露法,下劃線標(biāo)記法和使用閉包;下部講高級(jí)模式(Advanced Patterns),如何實(shí)現(xiàn)靜態(tài)方法和屬性,常量還有其他一些知識(shí)點(diǎn)。
封裝是面向?qū)ο笳Z言很基本也是很有用的特性,雖然javascript也可以稱的上是面向?qū)ο笳Z言,但他對(duì)封裝的支持并不是很好,不像其他語言,只要使用private、protected就可以實(shí)現(xiàn)。但這并不是說就沒有辦法了,下面我就介紹下如何在javascript中實(shí)現(xiàn)封裝。
一、基本模式(basic patterns),主要包括三種方式:完全暴露法,下劃線標(biāo)記法和使用閉包。(閉包是個(gè)很重要,也是很難的概念,有興趣的朋友可以去網(wǎng)上找資料,我博客里也轉(zhuǎn)載了別人的文章)。
這里我們以book類作為例子,需要?jiǎng)?chuàng)建和初始化book類。
// Book(isbn, title, author)
var theHobbit = new Book('0-395-07122-4', 'The Hobbit', 'J. R. R. Tolkien');
theHobbit.display(); // Outputs the data by creating and populating an HTML element.
1.完全暴露法:
創(chuàng)建book類可以用最傳統(tǒng)的構(gòu)造函數(shù)方式,
var Book = function(isbn, title, author) {
if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this.isbn = isbn;
//代碼中 || 的作用是 如果title無值,則會(huì)把'No title specified'賦給 this.title。這種方式很好用,大家可以在自己的代碼中使用。
this.title = title || 'No title specified';
this.author = author || 'No author specified';
}
Book.prototype = {
//驗(yàn)證isbn函數(shù)
checkIsbn: function(isbn) {
...
},
//獲取isbn
getIsbn: function() {
return this.isbn;
},
//設(shè)置isbn
setIsbn: function(isbn) {
if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this.isbn = isbn;
},
//獲取title
getTitle: function() {
return this.title;
},
//設(shè)置title
setTitle: function(title) {
this.title = title || 'No title specified';
},
//獲取作者
getAuthor: function() {
return this.author;
},
//設(shè)置作者
setAuthor: function(author) {
this.author = author || 'No author specified';
},
//顯示函數(shù)
display: function() {
...
}
};
代碼有點(diǎn)多,我在這里簡(jiǎn)單講解下。javascript中創(chuàng)建類和c#,java有點(diǎn)不同,c#,java會(huì)把所有方法和屬性包在一個(gè)類文件里面,比如說
public class book()
{
private string isbn;
public string ISBN
{
set
{
this.isbn=value;
}
get
{
return this.isbn;
}
}
...
private bool CheckIsbn(string isdn)
{
......
}
......
public void Display()
{
......
}
}
javascript也可以用這種方式,但是推薦使用我上面使用的把屬性定義到類定義函數(shù)(或者叫構(gòu)造函數(shù)),方法定義到prototype對(duì)象中,這種做法性能要好些,至于原因大家可以去google。
上面的js代碼想實(shí)現(xiàn)的功能是,定義一個(gè)book類,類里面包括三個(gè)私有變量(或者叫屬性)isbn,title,author,一個(gè)私有方法checkIsbn,幾個(gè)公有方法getIsdn,setIsdn,...display。想法是好的,但是現(xiàn)實(shí)是殘酷的,其實(shí)那些私有屬性或者方法根本一點(diǎn)都不私有。比如說,theHobbit.isbn = '978-0261103283';你可以用這種方式為isbn賦值,不會(huì)報(bào)錯(cuò)而且絕對(duì)成功。原因就是javascript沒有private方式去實(shí)現(xiàn)對(duì)特定對(duì)象的私有化。此外這種實(shí)現(xiàn)方式在使用時(shí)也會(huì)造成困惑,到底類的創(chuàng)建者想暴露哪些屬性和方法呢?下面介紹第一種改進(jìn)辦法,下劃線標(biāo)記法。
2.下劃線標(biāo)記法:
var Book = function(isbn, title, author) {
// Constructor code.
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
//驗(yàn)證isbn函數(shù)
_checkIsbn: function(isbn) {
...
},
//獲取isbn
getIsbn: function() {
return this._isbn;
},
//設(shè)置isbn
setIsbn: function(isbn) {
if(!this._checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this._isbn = isbn;
},
...
//顯示函數(shù)
display: function() {
...
}
};
其實(shí)就是在所有想實(shí)現(xiàn)私有的屬性或者方法前面加了下劃線_,沒別的操作。這種方法并沒有實(shí)現(xiàn)真正的私有化,theHobbit._isbn = '978-0261103283';這樣操作照樣成功,這種方式最大的意義在于告訴類的使用者,作者本意上想暴露哪些對(duì)象,不想暴露哪些。但是使用者是否按照作者的想法去做,作者是控制不了的。
那有沒有辦法實(shí)現(xiàn)真正的私有化呢,答案是有的,就是利用閉包。
3.使用閉包:
javascript之所以能實(shí)現(xiàn)真正的封裝,是和他特有的函數(shù)作用域,函數(shù)支持內(nèi)部函數(shù),還有閉包分不開的。大家可以去網(wǎng)上搜集相關(guān)知識(shí)加深理解。
下面首先說的就是函數(shù)作用域,在javascript中如果在一個(gè)函數(shù)內(nèi)部定義了一個(gè)變量,那么函數(shù)外部是沒有辦法訪問的。其實(shí)在javascript中實(shí)現(xiàn)私有屬性或者方法就是利用了它這一特殊屬性。例子:
function foo() {
var a = 10;
function bar() {
a *= 2;
}
bar();
return a;
}
在上面的例子中函數(shù)foo在內(nèi)部定義了變量a和方法bar,在foo外部是無法訪問到a和bar的,但是因?yàn)閍和bar都定義在foo內(nèi)部,但bar是可以訪問到a的。那么有沒有辦法能在foo外部訪問到bar呢,答案是有的,就是使用閉包。
function foo() {
var a = 10;
function bar() {
a *= 2;
return a;
}
return bar;
}
var baz = foo(); // baz is now a reference to function bar.
baz(); // returns 20.
baz(); // returns 40.
baz(); // returns 80.
var blat = foo(); // blat is another reference to bar.
blat(); // returns 20, because a new copy of a is being used.
這就是在前面提到的javascript函數(shù)支持內(nèi)部函數(shù)。內(nèi)部函數(shù)bar可以訪問私有變量a,函數(shù)foo又把內(nèi)部函數(shù)bar拋出給baz,baz就可以訪問到內(nèi)部變量a了,這就實(shí)現(xiàn)了閉包。大家一看也就明白了,這樣其實(shí)就實(shí)現(xiàn)了私有變量和方法?;氐轿覀兦懊娴腷ook例子,實(shí)現(xiàn)如下:
var Book = function(newIsbn, newTitle, newAuthor) {
// implements Publication
// Private attributes.
var isbn, title, author;
// Private method.
function checkIsbn(isbn) {
...
}
// Privileged methods.
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function() {
return title;
};
this.setTitle = function(newTitle) {
title = newTitle || 'No title specified';
};
this.getAuthor = function() {
return author;
};
this.setAuthor = function(newAuthor) {
author = newAuthor || 'No author specified';
};
// Constructor code.
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};
上述代碼就實(shí)現(xiàn)了 isbn, title, author和checkIsbn的私有化,外部是決定不能直接訪問到的。如需訪問 isbn, title, author只能通過對(duì)象級(jí)的方法getTitle,setTitle...。比如要給isbn賦值,只能用theHobbit.setIsbn = '978-0261103283';,如果你還用theHobbit._isbn = '978-0261103283';,對(duì)不起要報(bào)錯(cuò)了。
好了,今天的內(nèi)容就講到這里了,希望對(duì)大家有幫助。
作者:下一站永遠(yuǎn)
封裝是面向?qū)ο笳Z言很基本也是很有用的特性,雖然javascript也可以稱的上是面向?qū)ο笳Z言,但他對(duì)封裝的支持并不是很好,不像其他語言,只要使用private、protected就可以實(shí)現(xiàn)。但這并不是說就沒有辦法了,下面我就介紹下如何在javascript中實(shí)現(xiàn)封裝。
一、基本模式(basic patterns),主要包括三種方式:完全暴露法,下劃線標(biāo)記法和使用閉包。(閉包是個(gè)很重要,也是很難的概念,有興趣的朋友可以去網(wǎng)上找資料,我博客里也轉(zhuǎn)載了別人的文章)。
這里我們以book類作為例子,需要?jiǎng)?chuàng)建和初始化book類。
復(fù)制代碼 代碼如下:
// Book(isbn, title, author)
var theHobbit = new Book('0-395-07122-4', 'The Hobbit', 'J. R. R. Tolkien');
theHobbit.display(); // Outputs the data by creating and populating an HTML element.
1.完全暴露法:
創(chuàng)建book類可以用最傳統(tǒng)的構(gòu)造函數(shù)方式,
復(fù)制代碼 代碼如下:
var Book = function(isbn, title, author) {
if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this.isbn = isbn;
//代碼中 || 的作用是 如果title無值,則會(huì)把'No title specified'賦給 this.title。這種方式很好用,大家可以在自己的代碼中使用。
this.title = title || 'No title specified';
this.author = author || 'No author specified';
}
Book.prototype = {
//驗(yàn)證isbn函數(shù)
checkIsbn: function(isbn) {
...
},
//獲取isbn
getIsbn: function() {
return this.isbn;
},
//設(shè)置isbn
setIsbn: function(isbn) {
if(!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this.isbn = isbn;
},
//獲取title
getTitle: function() {
return this.title;
},
//設(shè)置title
setTitle: function(title) {
this.title = title || 'No title specified';
},
//獲取作者
getAuthor: function() {
return this.author;
},
//設(shè)置作者
setAuthor: function(author) {
this.author = author || 'No author specified';
},
//顯示函數(shù)
display: function() {
...
}
};
代碼有點(diǎn)多,我在這里簡(jiǎn)單講解下。javascript中創(chuàng)建類和c#,java有點(diǎn)不同,c#,java會(huì)把所有方法和屬性包在一個(gè)類文件里面,比如說
復(fù)制代碼 代碼如下:
public class book()
{
private string isbn;
public string ISBN
{
set
{
this.isbn=value;
}
get
{
return this.isbn;
}
}
...
private bool CheckIsbn(string isdn)
{
......
}
......
public void Display()
{
......
}
}
javascript也可以用這種方式,但是推薦使用我上面使用的把屬性定義到類定義函數(shù)(或者叫構(gòu)造函數(shù)),方法定義到prototype對(duì)象中,這種做法性能要好些,至于原因大家可以去google。
上面的js代碼想實(shí)現(xiàn)的功能是,定義一個(gè)book類,類里面包括三個(gè)私有變量(或者叫屬性)isbn,title,author,一個(gè)私有方法checkIsbn,幾個(gè)公有方法getIsdn,setIsdn,...display。想法是好的,但是現(xiàn)實(shí)是殘酷的,其實(shí)那些私有屬性或者方法根本一點(diǎn)都不私有。比如說,theHobbit.isbn = '978-0261103283';你可以用這種方式為isbn賦值,不會(huì)報(bào)錯(cuò)而且絕對(duì)成功。原因就是javascript沒有private方式去實(shí)現(xiàn)對(duì)特定對(duì)象的私有化。此外這種實(shí)現(xiàn)方式在使用時(shí)也會(huì)造成困惑,到底類的創(chuàng)建者想暴露哪些屬性和方法呢?下面介紹第一種改進(jìn)辦法,下劃線標(biāo)記法。
2.下劃線標(biāo)記法:
復(fù)制代碼 代碼如下:
var Book = function(isbn, title, author) {
// Constructor code.
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
//驗(yàn)證isbn函數(shù)
_checkIsbn: function(isbn) {
...
},
//獲取isbn
getIsbn: function() {
return this._isbn;
},
//設(shè)置isbn
setIsbn: function(isbn) {
if(!this._checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.');
this._isbn = isbn;
},
...
//顯示函數(shù)
display: function() {
...
}
};
其實(shí)就是在所有想實(shí)現(xiàn)私有的屬性或者方法前面加了下劃線_,沒別的操作。這種方法并沒有實(shí)現(xiàn)真正的私有化,theHobbit._isbn = '978-0261103283';這樣操作照樣成功,這種方式最大的意義在于告訴類的使用者,作者本意上想暴露哪些對(duì)象,不想暴露哪些。但是使用者是否按照作者的想法去做,作者是控制不了的。
那有沒有辦法實(shí)現(xiàn)真正的私有化呢,答案是有的,就是利用閉包。
3.使用閉包:
javascript之所以能實(shí)現(xiàn)真正的封裝,是和他特有的函數(shù)作用域,函數(shù)支持內(nèi)部函數(shù),還有閉包分不開的。大家可以去網(wǎng)上搜集相關(guān)知識(shí)加深理解。
下面首先說的就是函數(shù)作用域,在javascript中如果在一個(gè)函數(shù)內(nèi)部定義了一個(gè)變量,那么函數(shù)外部是沒有辦法訪問的。其實(shí)在javascript中實(shí)現(xiàn)私有屬性或者方法就是利用了它這一特殊屬性。例子:
復(fù)制代碼 代碼如下:
function foo() {
var a = 10;
function bar() {
a *= 2;
}
bar();
return a;
}
在上面的例子中函數(shù)foo在內(nèi)部定義了變量a和方法bar,在foo外部是無法訪問到a和bar的,但是因?yàn)閍和bar都定義在foo內(nèi)部,但bar是可以訪問到a的。那么有沒有辦法能在foo外部訪問到bar呢,答案是有的,就是使用閉包。
復(fù)制代碼 代碼如下:
function foo() {
var a = 10;
function bar() {
a *= 2;
return a;
}
return bar;
}
var baz = foo(); // baz is now a reference to function bar.
baz(); // returns 20.
baz(); // returns 40.
baz(); // returns 80.
var blat = foo(); // blat is another reference to bar.
blat(); // returns 20, because a new copy of a is being used.
這就是在前面提到的javascript函數(shù)支持內(nèi)部函數(shù)。內(nèi)部函數(shù)bar可以訪問私有變量a,函數(shù)foo又把內(nèi)部函數(shù)bar拋出給baz,baz就可以訪問到內(nèi)部變量a了,這就實(shí)現(xiàn)了閉包。大家一看也就明白了,這樣其實(shí)就實(shí)現(xiàn)了私有變量和方法?;氐轿覀兦懊娴腷ook例子,實(shí)現(xiàn)如下:
復(fù)制代碼 代碼如下:
var Book = function(newIsbn, newTitle, newAuthor) {
// implements Publication
// Private attributes.
var isbn, title, author;
// Private method.
function checkIsbn(isbn) {
...
}
// Privileged methods.
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function() {
return title;
};
this.setTitle = function(newTitle) {
title = newTitle || 'No title specified';
};
this.getAuthor = function() {
return author;
};
this.setAuthor = function(newAuthor) {
author = newAuthor || 'No author specified';
};
// Constructor code.
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};
上述代碼就實(shí)現(xiàn)了 isbn, title, author和checkIsbn的私有化,外部是決定不能直接訪問到的。如需訪問 isbn, title, author只能通過對(duì)象級(jí)的方法getTitle,setTitle...。比如要給isbn賦值,只能用theHobbit.setIsbn = '978-0261103283';,如果你還用theHobbit._isbn = '978-0261103283';,對(duì)不起要報(bào)錯(cuò)了。
好了,今天的內(nèi)容就講到這里了,希望對(duì)大家有幫助。
作者:下一站永遠(yuǎn)
您可能感興趣的文章:
- 小議javascript 設(shè)計(jì)模式 推薦
- javascript 通過封裝div方式彈出div窗體
- Jquery作者John Resig自己封裝的javascript 常用函數(shù)
- javascript 面向?qū)ο笕吕砭氈當(dāng)?shù)據(jù)的封裝
- JavaScript 設(shè)計(jì)模式 安全沙箱模式
- javascript的函數(shù)、創(chuàng)建對(duì)象、封裝、屬性和方法、繼承
- Javascript 面向?qū)ο缶幊蹋ㄒ唬?封裝
- Javascript 面向?qū)ο螅ǘ┓庋b代碼
- javascript設(shè)計(jì)模式 接口介紹
- javascript移動(dòng)設(shè)備Web開發(fā)中對(duì)touch事件的封裝實(shí)例
- 原生Javascript封裝的一個(gè)AJAX函數(shù)分享
- JavaScript設(shè)計(jì)模式之原型模式(Object.create與prototype)介紹
- JavaScript設(shè)計(jì)模式之工廠方法模式介紹
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式(封裝)
相關(guān)文章
利用遞增的數(shù)字返回循環(huán)漸變的顏色的js代碼
其實(shí)很久前就想寫一個(gè)這樣的函數(shù)了。因?yàn)楹芏鄷r(shí)候需要利用遞增數(shù)字返回一個(gè)漸變顏色序列,今天終于完成了。2008-10-10javascript中for/in循環(huán)及使用技巧
如果您希望一遍又一遍地運(yùn)行相同的代碼,并且每次的值都不同,那么使用循環(huán)是很方便的,本篇文章給大家介紹javascript中for/in循環(huán)及使用技巧 ,需要的朋友可以參考下2015-09-09Microsfot .NET Framework4.0框架 安裝失敗的解決方法
今天在安裝.NET Framework 4.0,安裝了半天結(jié)果提示未安裝成功,提示原因是服務(wù)未啟動(dòng)了,error code: (0x80070643), "安裝時(shí)發(fā)生嚴(yán)重錯(cuò)誤 "2013-08-08layui問題之渲染數(shù)據(jù)表格時(shí),僅出現(xiàn)10條數(shù)據(jù)的解決方法
今天小編就為大家分享一篇layui問題之渲染數(shù)據(jù)表格時(shí),僅出現(xiàn)10條數(shù)據(jù)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09在table中插入多行的js代碼(與insertAdjacentHTML相似的功能)
在table中插入多行,能使用與insertAdjacentHTML相似的功能2010-06-06