javascript面向?qū)ο笕筇卣髦庋b實(shí)例詳解
本文實(shí)例講述了javascript面向?qū)ο笕筇卣髦庋b。分享給大家供大家參考,具體如下:
封裝
封裝(Encapsulation):就是把對(duì)象內(nèi)部數(shù)據(jù)和操作細(xì)節(jié)進(jìn)行隱藏。很多面向?qū)ο笳Z(yǔ)言都支持封裝特性,提供關(guān)鍵字如private來(lái)隱藏某些屬性和方法。要想訪問(wèn)被封裝對(duì)象中的數(shù)據(jù),只能使用對(duì)象專(zhuān)門(mén)提供的對(duì)外接口,這個(gè)接口一般為方法。調(diào)用該方法能夠獲取對(duì)象內(nèi)部數(shù)據(jù)。
在JavaScript語(yǔ)言中沒(méi)有提供專(zhuān)門(mén)的信息封裝關(guān)鍵字,不過(guò)可以使用閉包來(lái)創(chuàng)建,只允許從對(duì)象內(nèi)部訪問(wèn)的方法和屬性。另外,接口也是數(shù)據(jù)封裝的一種工具,接口提供了外界訪問(wèn)方法的約定。在應(yīng)用開(kāi)發(fā)中,所有類(lèi)都應(yīng)定義接口,類(lèi)只向外提供已實(shí)現(xiàn)接口中規(guī)定的方法,任何別的方法都是隱藏的。其所有屬性都是私有的,外界只能通過(guò)接口中定義的存取操作與之打交道。
---引自 《jQuery開(kāi)發(fā)從入門(mén)到精通》,不過(guò)原書(shū)有錯(cuò)誤,或者可能是我錯(cuò)買(mǎi)了盜版,不過(guò)下面代碼都是經(jīng)過(guò)我修改的,沒(méi)有問(wèn)題。
被動(dòng)封裝
被動(dòng)封裝:就是對(duì)對(duì)象內(nèi)部數(shù)據(jù)進(jìn)行適當(dāng)約定,這種約定具有很強(qiáng)的主觀性,沒(méi)有強(qiáng)制性保證,主要針對(duì)公共對(duì)象而言。一般來(lái)說(shuō),JavaScript所包含的數(shù)據(jù)都是公開(kāi)的,沒(méi)有隱私可言,其中的信息可以隨意被訪問(wèn)。下面給出一個(gè)例子來(lái)闡述:
var Person = function (name,gender) {
if(name === undefined) {
throw new Error("name is necessary")
} else {
this.name = name;
}
if(gender === undefined) {
throw new Error("gender is necessary")
} else {
this.gender = gender;
}
}
var p = new Person("Tom",1); // 如果我們實(shí)例化的時(shí)候,如果傳遞參數(shù)錯(cuò)誤了,將會(huì)報(bào)異常
為了數(shù)據(jù)安全,代碼中適當(dāng)?shù)脑黾恿艘恍l件限制,避免非法信息侵入。
var Animal = function (species) {
if(!this.checkSpecies(species)) {
throw new Error('species is illegal');
} else {
this.species = species;
}
}
Animal.prototype = {
checkSpecies:function(species){
// 檢測(cè) species , 參數(shù)為 species , 合法返回true ,這里可自定義檢測(cè)邏輯
// 這里先讓它直接返回 true
return true;
}
}
var ani = new Animal("dog");
從更安全和擴(kuò)展的角度來(lái)說(shuō),凡是類(lèi)都應(yīng)該定義接口,這樣能夠保證數(shù)據(jù)存取更加安全,同時(shí)也方便團(tuán)隊(duì)的合作。內(nèi)部私有方法檢測(cè)和接口措施可在一定程度上保護(hù)對(duì)象內(nèi)部數(shù)據(jù),但是他們也存在一個(gè)致命漏洞(這些屬性和方法可以被公開(kāi)重置,面對(duì)公開(kāi)覆蓋屬性和方法值,任何人都無(wú)法阻止這種行為。),比如這樣的:
var Util = function() {};
Util.prototype = {
_say:function(){
console.log("這里有一個(gè)私有的成員 _say方法");
}
}
// 下面我們來(lái)修改它
Util.prototype._say = function(){
console.log('哈哈,我已經(jīng)被修改了');
}
// 開(kāi)始檢測(cè)
var util = new Util;
util._say(); // 哈哈,我已經(jīng)被修改了
同時(shí)內(nèi)部檢測(cè)和接口可以在一定程度上占用了系統(tǒng)開(kāi)銷(xiāo),這個(gè)問(wèn)題也是必須認(rèn)真考慮的。
就像上面的代碼那樣,一般我們約定了區(qū)別公共和私有成員,就是在一些方法和屬性名稱(chēng)前或者后加下劃線,來(lái)表示私有。
下劃線命名是一種約定俗稱(chēng)的命名規(guī)范,表示一個(gè)屬性和方法僅供內(nèi)部使用,直接訪問(wèn)可能會(huì)導(dǎo)致意外發(fā)生。
以上數(shù)據(jù)保護(hù)方式都是被動(dòng)性防御,因?yàn)樗麄冎皇且环N約定,只有在遵守的時(shí)候才有效果。主要適用于非敏感性的內(nèi)部方法和屬性。
主動(dòng)封裝
在js中,因?yàn)楹瘮?shù)有作用域,在函數(shù)內(nèi)部聲明的變量,在函數(shù)外部無(wú)法訪問(wèn)。而所以的私有和公有的區(qū)別就在于在對(duì)象外部是否可以訪問(wèn)。所以實(shí)現(xiàn)封裝的最佳選擇是使用函數(shù)的作用域。舉例:
function haha() {
var n = 1;
function hehe() {
n++;
}
hehe();
return n;
}
console.log(haha()); // 2
函數(shù)haha內(nèi)部有私有變量n,在此作用域中,hehe能夠訪問(wèn)n,而haha外部的任何函數(shù)都無(wú)法訪問(wèn)haha里的n。
同樣的,我們?cè)诤瘮?shù)內(nèi)部返回hehe,就可以在外部來(lái)調(diào)用haha私有函數(shù)hehe。
function haha() {
var n = 1;
function hehe() {
return ++n;
}
return hehe;
}
console.log(haha()()); // 2
函數(shù)作用域內(nèi)部的方法無(wú)法被外界訪問(wèn),但是在函數(shù)作用域內(nèi)部的其他公共方法可以訪問(wèn)到它們,于是利用公共方法為中轉(zhuǎn)站,可以巧妙的把內(nèi)部私有方法公開(kāi)化。因此這些公共方法也被稱(chēng)為特權(quán)方法,即在方法前面加this關(guān)鍵字。
使用這種方式創(chuàng)建的對(duì)象具有真正的封裝性如:
function A() {
// 私有方法 _xx
function _xx(){}
// 公有方法
this.xx = function() {
return _xx;
}
}
A.prototype = {
other:function() {
console.log("other方法代表不需要訪問(wèn)私有屬性和方法的方法,一般放在原型里");
}
}
但是這種方式也有一些缺點(diǎn):
① 新生成的每個(gè)實(shí)例對(duì)象都會(huì)拷貝構(gòu)造函數(shù)中的屬性和方法,而私有的_xx 無(wú)疑在每次實(shí)例化的時(shí)候都會(huì)重復(fù)拷貝,這樣會(huì)占用大量?jī)?nèi)存,所以不適合大量使用,僅在必要時(shí)適當(dāng)使用
② 不利于類(lèi)的繼承,所有派生的子類(lèi)都不能訪問(wèn)超類(lèi)中的私有屬性和方法。不過(guò)可以使用特權(quán)方法來(lái)訪問(wèn)超類(lèi)中的私有屬性和方法。舉個(gè)例子:
// B 是超類(lèi)
function B() {
var _private = 1;
function _checkPrivate() {
return _private;
}
this.checkPrivate = function() {
return _checkPrivate;
}
}
// C 是派生類(lèi)
function C() {
B.call(this); // 用call 實(shí)現(xiàn)繼承 ,后面我們會(huì)講到如何繼承
// this.private = _private; // 這里嘗試訪問(wèn)超類(lèi)的私有屬性,因?yàn)闊o(wú)法訪問(wèn),會(huì)報(bào)錯(cuò)
}
// 實(shí)例化 派生類(lèi)C
var c = new C();
// console.log(c.private); // 和在C內(nèi)部的嘗試一樣,是無(wú)法訪問(wèn)的,報(bào)錯(cuò)
// 不過(guò)可以用下面的方法訪問(wèn)超類(lèi)中的私有屬性
console.log(c.checkPrivate()()); // 1
靜態(tài)方法
在面向?qū)ο缶幊讨?,大多?shù)的方法和屬性與類(lèi)的實(shí)例產(chǎn)生聯(lián)系。還有一種情況是,靜態(tài)屬性和方法是與類(lèi)本身直接聯(lián)系,可直接從類(lèi)訪問(wèn),也就是說(shuō),靜態(tài)成員在類(lèi)上操作,而不是實(shí)例上。在JavaScript中的Math和Global都是靜態(tài)對(duì)象,不需要實(shí)例化就可直接訪問(wèn)。
類(lèi)的靜態(tài)成員,包括私有和公共兩種。他們?cè)谙到y(tǒng)中只有一份副本,意思就是說(shuō)他們不會(huì)被分成多份傳遞給不同的實(shí)例對(duì)象。而是通過(guò)函數(shù)指針進(jìn)行引用。書(shū)上有個(gè)例子非常好,下面舉例:
var F = (function(){ // 把閉包函數(shù)賦給F,返回一個(gè)構(gòu)造函數(shù)
var _a = 1; // 定義一個(gè)閉包體內(nèi)的私有變量
this.a = _a; // 閉包體內(nèi)的公共屬性 a
this.get1 = function(){ // 閉包體內(nèi)的公共方法get1
return _a;
}
this.set1 = function(x){ // 閉包體內(nèi)的公共方法set1
_a = x;
};
return function(){ // 返回一個(gè)構(gòu)造函數(shù),構(gòu)造函數(shù)也是函數(shù),更是對(duì)象(相當(dāng)于一個(gè)類(lèi))
this.get2 = function() { // 類(lèi)的get2方法
return _a;
};
this.set2 = function(x) { // 類(lèi)的set2方法
_a = x;
};
}
})();
// 定義類(lèi)的靜態(tài)公共方法和屬性
F.get3 = function(){ // 定義一個(gè)靜態(tài)方法get3
return get1(); // 這里可以直接使用 F內(nèi)的公共方法get1
}
F.set3 = function(x) { // 定義一個(gè)靜態(tài)方法set3
return set1(x); // 同get1
}
// 下面開(kāi)始測(cè)試
var f = new F(); // 實(shí)例化這個(gè)類(lèi)
console.log(f.get2()); // 1 用實(shí)例對(duì)象訪問(wèn)公共方法get2
F.set3(3); // 調(diào)用靜態(tài)方法set3
console.log(F.get3()); // 3
F.A = ++a; // 定義一個(gè)靜態(tài)屬性A
console.log(F.A); // 2
我們推薦使用這種閉包的封裝方式
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼運(yùn)行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專(zhuān)題:《javascript面向?qū)ο笕腴T(mén)教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。
- 學(xué)習(xí)Javascript面向?qū)ο缶幊讨庋b
- Javascript 面向?qū)ο缶幊蹋ㄒ唬?封裝
- javascript 面向?qū)ο缶幊袒A(chǔ):封裝
- Javascript之面向?qū)ο?-封裝
- js實(shí)現(xiàn)對(duì)ajax請(qǐng)求面向?qū)ο蟮姆庋b
- javascript 面向?qū)ο蠓庋b與繼承
- javascript面向?qū)ο蟀b類(lèi)Class封裝類(lèi)庫(kù)剖析
- Javascript 面向?qū)ο螅ǘ┓庋b代碼
- 面向?qū)ο蟮腏avascript之三(封裝和信息隱藏)
- javascript對(duì)XMLHttpRequest異步請(qǐng)求的面向?qū)ο蠓庋b
- javascript 面向?qū)ο笕吕砭氈當(dāng)?shù)據(jù)的封裝
- JS面向?qū)ο缶幊袒A(chǔ)篇(二) 封裝操作實(shí)例詳解
相關(guān)文章
js input輸入百分號(hào)保存數(shù)據(jù)庫(kù)失敗的解決方法
這篇文章主要介紹了js input輸入百分號(hào)保存數(shù)據(jù)庫(kù)失敗的解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-05-05
js學(xué)習(xí)總結(jié)之DOM2兼容處理順序問(wèn)題的解決方法
這篇文章主要為大家詳細(xì)介紹了DOM2兼容處理順序問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
jQuery Mobile動(dòng)態(tài)刷新頁(yè)面樣式的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇jQuery Mobile動(dòng)態(tài)刷新頁(yè)面樣式的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05
js ondocumentready onmouseover onclick onmouseout 樣式
下面都是一些上面的事件觸發(fā)的事先定義的代碼。2010-07-07
JavaScript中獲取高度和寬度函數(shù)總結(jié)
這篇文章主要介紹了JavaScript中獲取高度和寬度函數(shù)總結(jié),例如獲取視窗大小、可見(jiàn)區(qū)域?qū)?、可?jiàn)區(qū)域高、獲取元素自身大小等,很方便的一個(gè)總結(jié),需要的朋友可以參考下2014-10-10
Bootstrap媒體對(duì)象學(xué)習(xí)使用
這篇文章主要為大家詳細(xì)介紹了Bootstrap媒體對(duì)象的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
JS開(kāi)發(fā)接入?deepseek?實(shí)現(xiàn)AI智能問(wèn)診
本文介紹了如何使用DeepSeek?API在UniApp項(xiàng)目中實(shí)現(xiàn)智能問(wèn)診功能,代碼示例展示了如何構(gòu)建請(qǐng)求并處理響應(yīng),本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-02-02

