從java到j(luò)avascript的快速入門

javajavascript的快速入門

本文專門為學(xué)過C語言的Java程序員起便,快速理解javascript語言的重要概念户秤,量身定做。筆者對javascript其他實(shí)現(xiàn)面向?qū)ο蟮姆绞剑M可能保持克制熊户,盡可能讓javascript的面向?qū)ο箫L(fēng)格與java對標(biāo)起來途事,幫助java程序員快速掌握javascript面向?qū)ο蟆?/p>

之所以有必要講解下javascript验懊,是因?yàn)槿缃竦?code>javascript是全能(全棧)的,除了運(yùn)行于瀏覽器尸变;還可以借助node運(yùn)行到服務(wù)器义图;還可以借助electron這類框架,開發(fā)桌面程序召烂;還可以React-Native碱工,開發(fā)手機(jī)程序;還有在類似cassandra服務(wù)器里面用javascript做嵌入式腳本語言奏夫。類似這樣的變化怕篷,還在不斷發(fā)生。

文章用到的代碼均托管在 githubdowngoon/java2javascript 上酗昼,可通過issue給我反饋 廊谓。

1. 從函數(shù)開始

要實(shí)現(xiàn)一個(gè)sumlen函數(shù),輸入兩個(gè)字符串麻削,返回它們的長度之和蒸痹。java代碼(完整代碼見C1Sumlen.java)是這樣的:

// 函數(shù)定義
int sumlen(String x, String y) {
  return x.length() + y.length();
}

// 函數(shù)調(diào)用
int sl = sumlen("hello", "world");

相比較javascript代碼(完整代碼見C1Sumlen.js)是這樣的:

// 函數(shù)定義
function sumlen(x, y) {
  return x.length + y.length;
}

// 函數(shù)調(diào)用
var sl = sumlen("hello", 'world');

兩者的相似與差異正如它們的名字(據(jù)說javascript當(dāng)初叫這個(gè)名字也正是想借一下java的勢頭):

  • 相似處:語法上很像。
  • 差異處:(1)JS是弱類型呛哟,Java是強(qiáng)類型叠荠;(2)JS函數(shù)需要function關(guān)鍵字,Java無需竖共。

弱類型

JS不像Java那樣蝙叛,定義變量x的時(shí)候,要指定數(shù)據(jù)類型是String公给,BooleanInteger借帘。JS形式上都是var(類似Java的Object是一切類的超類)蜘渣,只不過當(dāng)它賦值后,就隱含了數(shù)據(jù)類型(可用系統(tǒng)typeof函數(shù)查看)肺然。如下詳細(xì)代碼見C1JsType.js:

var x;  // 類型 undefined
x = 'helloworld';  // 類型 string
x = 3.14;  // 類型 number
x = true;  // 類型 boolean
x = ['helloworld', 3.14, true];   // 屬性 數(shù)組Object

function關(guān)鍵字

正因?yàn)镴S是弱類型蔫缸,于是定義函數(shù)的時(shí)候,函數(shù)簽名時(shí)的輸入?yún)?shù)和輸出參數(shù)就沒必要都帶個(gè)var了际起,這樣代碼(詳細(xì)見C1JsFunc.js)更簡潔:

// 無輸入拾碌,無輸出
function f_in0_ou0() {
    console.log('無輸入,無輸出');
}

// 無輸入街望,有輸出
function f_in0_ou1() {
    return 'helloworld';
}

// 有輸入校翔,有輸出
function f_in1_ou1(x) {
    return x.length;
}

// 有輸入,無輸出
function f_in0_ou1() {
    return Math.random();
}

函數(shù)指針與調(diào)用運(yùn)算符

弱類型談到無論是布爾類型灾前,數(shù)字類型還是字符串類型都可以賦值給var防症。在JS中,function也是一種類型哎甲,它也可以賦值給var變量蔫敲,這點(diǎn)非常類似C語言的函數(shù)指針或說Java語言的interface。詳細(xì)代碼見C1JsFuncPointer.js

function sumlen(x, y) {
    return x.length + y.length;
};

var f = sumlen; // 函數(shù)賦值給變量(類似函數(shù)指針)

sumlen('hello', 'world');  //  按函數(shù)名稱調(diào)用
f('hello', 'world');  // 按函數(shù)指針調(diào)用

f('hello', 'world'); 語法就是在函數(shù)指針后面加一個(gè)()調(diào)用運(yùn)算符炭玫,就表示調(diào)用奈嘿。

優(yōu)先運(yùn)算符

函數(shù)指針后面的()調(diào)用運(yùn)算符。實(shí)際上我們還可以在函數(shù)指針前面加()吞加,表示優(yōu)先運(yùn)算符裙犹。例如:

f('hello', 'world');  // 按函數(shù)指針調(diào)用
(f)('hello', 'world');  // 函數(shù)指針加了一個(gè) 優(yōu)先運(yùn)算符
((f))('hello', 'world');  // 加2個(gè)優(yōu)先運(yùn)算符

它就類似算數(shù)運(yùn)算表達(dá)式: 1*2+3*4 也可更加清楚的寫為 (1*2)+(3*4)

匿名函數(shù)

var g = function (x, y) {
    return x.length + y.length;
}
console.log('匿名函數(shù): ' + g('hello', 'world'));

JS的函數(shù)衔憨,還可以不用起名字伯诬,用function聲明后,立即賦值給一個(gè)變量(如果不賦值給一個(gè)變量巫财,以后就沒法調(diào)用了)。這種不起名字的函數(shù)就叫匿名函數(shù)哩陕。

匿名函數(shù)的立即調(diào)用

前面我們看到平项,函數(shù)可以賦值給一個(gè)變量(函數(shù)指針),然后通過函數(shù)指針可以調(diào)用函數(shù):

var f = sumlen;  // 函數(shù)賦值給變量
f('hello', 'world');  // 按函數(shù)指針調(diào)用

也提到如果是匿名函數(shù)定義悍及,必須賦值給一個(gè)變量闽瓢,不然以后沒有調(diào)用句柄。但是我們有沒有想過心赶,如果定義的時(shí)候扣讼,就立即調(diào)用呢?這樣不就不需要調(diào)用句柄么缨叫?答案:的確可以椭符。

(function (x, y) {
    console.log('匿名函數(shù)的立即調(diào)用:' + x + ',' + y);
    return x.length + y.length;
})('hello', 'world');

這個(gè)匿名函數(shù)是帶兩個(gè)輸入?yún)?shù)的荔燎,也可以不帶輸入?yún)?shù)的:

(function() {
    console.log('不帶參數(shù)的匿名函數(shù)的立即調(diào)用');
})();

上述結(jié)構(gòu)是這樣的:

  • 匿名函數(shù):function() {}
  • 匿名函數(shù)調(diào)用:先用了一個(gè)優(yōu)先運(yùn)算符,把函數(shù)優(yōu)先起來销钝,(function(){})有咨;接著使用調(diào)用運(yùn)算符(function(){})()蒸健。

2. 插播JS對象

對象定義

前面談了JS的函數(shù)座享,還沒完,但我們先放一放似忧。插播下JS對象渣叛。對Java程序員來說,JS對象不會(huì)陌生盯捌,它非常類似JSON數(shù)據(jù)(因?yàn)镴SON就是從JS來的)淳衙,具體代碼詳見C2JsObj.js

var x = {'name': 'zhangsan', 'age': 25, 'male': true};

當(dāng)然,為了方便挽唉,JS還允許它寫成如下格式(屬性名可以不用引號(hào)):

var x = {name: 'zhangsan', age: 25, male: true};

屬性讀取

如果要引用它的屬性呢滤祖?下面兩種方式都可以:

x.name   // 很像Java的public成員,也像C的結(jié)構(gòu)體成員
x['name']   // 居然還可以類似Java的Map

恐怕讓Java程序員吃驚的是對象的屬性還可以x['name']引用瓶籽。其實(shí)Java也可以的匠童,只不過是要用反射機(jī)制。
自此JS的變量塑顺,可以被賦值以下各種類型:

var x;
x = 3.14;
x = `helloworld`;
x = true;
x = function() {};
x = {'name': 'zhangsan', 'age': 25, 'male': true};

屬性追加

JS對象還有個(gè)特點(diǎn)汤求,它可以隨時(shí)追加屬性。比如上面的人除了姓名严拒、年齡和性別外扬绪,我們還可以加一個(gè)學(xué)歷信息:

x.edu = 'master';
console.log(x); // 它輸出: { name: 'zhangsan', age: 25, male: true, edu: 'master' }

對Java程序員看來,JS對象裤唠,有點(diǎn)像Class類new出來的對象挤牛,又有點(diǎn)像Map這種字典類或者是基于反射的類,可以隨時(shí)加屬性种蘸。

屬性遍歷

如果要遍歷對象x下的所有屬性墓赴,可以類似Java的Map遍歷一樣:

for (var f in x) {
    console.log(f);
}

3. 對象與函數(shù)糾纏

有了函數(shù)對象的基本知識(shí),我們接著看看它們之間的糾纏組合航瞭。詳細(xì)代碼見C3FuncObj.js

對象的屬性是一個(gè)函數(shù)

剛才的var x = {name: 'zhangsan', age: 25, male: true};诫硕,對象的屬性都是普通類型的數(shù)據(jù),而對象屬性本質(zhì)上就是一個(gè)變量刊侯,這個(gè)變量既可指向普通數(shù)據(jù)章办,也可指向另一個(gè)對象,還可以指向一個(gè)函數(shù)。

var x = {
    name: 'zhangsan',
    age: 25,
    male: true,
    selfIntro: function() {
        console.log('我叫'+this.name+',今年'+this.age+'歲藕届,性別'+(this.male?'男':'女'));
    },
    sayHi: function() {
        console.log("Hello, World");
    },
    parents: [
        {'name': 'wangyi', 'age': 42},
        {name: 'maliu', 'age': 40}
    ]
};

console.log(x.selfIntro); // 打印出函數(shù)
x.selfIntro();
x.sayHi();

這里的sayHi屬性和selfIntro屬性都指向一個(gè)匿名函數(shù)挪蹭;而parents屬性指向一個(gè)對象數(shù)組。

注意

selfIntro方法中翰舌,為了訪問name等屬性嚣潜,還使用到了this關(guān)鍵字,表示本對象的引用椅贱。

返回對象的函數(shù)

截止目前懂算,我們之前講的函數(shù),要么沒有返回值庇麦,要么返回的只是普通數(shù)據(jù)计技。這里我們要在一個(gè)函數(shù)調(diào)用后,返回一個(gè)對象:

function Person() { // 函數(shù)名稱的首字母故意大寫
    return {'name': 'zhangsan', 'age': 25, 'male': true};
};

var p1 = Person(); // 函數(shù)調(diào)用第一次山橄,返回一個(gè)對象
var p2 = Person(); // 函數(shù)調(diào)用第二次垮媒,返回第二個(gè)對象
console.log(p1==p2); // 打印false,表示的確是兩個(gè)不同對象

代碼var p1 = Person();航棱,完全是函數(shù)的一次調(diào)用睡雇,只不過這個(gè)函數(shù)的首字母大寫了,就會(huì)馬上讓Java程序員聯(lián)想到Person p1 = new Person();饮醇。我們還可以引入3個(gè)輸入變量它抱,把Person函數(shù)改寫下:

function Person(name, age, male) {
    return {'name': name, 'age': age, 'male': male};
};

返回函數(shù)的函數(shù)

前面的章節(jié),大家感受到在JS里函數(shù)也是個(gè)對象朴艰,可以賦值給var x观蓄。那么我們有沒有想過,如果一個(gè)函數(shù)的返回值是另外一個(gè)函數(shù)呢祠墅?

function Person(n, a, m) { // 函數(shù)的輸入?yún)?shù)
    var name = n;
    var age = a;  // 定義局部變量(函數(shù)內(nèi)的)
    var male = m;

    return function() { // 函數(shù)的返回值是一個(gè)匿名函數(shù)
        return '我叫'+name+',今年'+age+'歲侮穿,性別'+(male?'男':'女');
    };
};

var pSelfIntro = Person('chenliu', 36, true);
console.log(pSelfIntro());

這段代碼是一個(gè)名叫Person的函數(shù),它有點(diǎn)特別的是函數(shù)調(diào)用后的返回值不是普通數(shù)據(jù)毁嗦,而是一個(gè)匿名函數(shù)(當(dāng)然這個(gè)匿名函數(shù)的返回值是一個(gè)組合字符串)亲茅。語句var pSelfIntro = Person('chenliu', 36, true);是把函數(shù)調(diào)用的結(jié)果(一個(gè)匿名函數(shù))賦值給了一個(gè)pSelfIntro變量,這個(gè)變量后面接一個(gè)()調(diào)用運(yùn)算符狗准,實(shí)現(xiàn)調(diào)用芯急,并把調(diào)用結(jié)果用console輸出。

函數(shù)嵌套另一個(gè)函數(shù)

剛才的例子驶俊,返回值是一個(gè)函數(shù)的時(shí)候,其實(shí)也意味著在JS中免姿,一個(gè)函數(shù)的內(nèi)部還可以定義另外一個(gè)函數(shù)饼酿。也就是函數(shù)嵌套
上述返回函數(shù)的函數(shù)改寫下,以便更清晰地展現(xiàn)函數(shù)嵌套的概念:

function PersonOuter(n, a, m) {
    var name = n;
    var age = a;  // 定義局部變量(函數(shù)內(nèi)的)
    var male = m;

    function PersonInner() { // 函數(shù)嵌套
        return '我叫'+name+',今年'+age+'歲故俐,性別'+(male?'男':'女');
    };

    return PersonInner; // 函數(shù)的返回值是一個(gè)帶名函數(shù)
};

var innerFunc = PersonOuter('chenliu', 36, true);
innerFunc(); // 函數(shù)調(diào)用

我們可以看到想鹰,在內(nèi)部函數(shù)可以訪問外部函數(shù)的變量。再來C3FuncObj-nestfun.js

var outer_var = 1;
function outer_fun() { // 外層函數(shù)定義
    var midd_var = 2;
    function inner_fun() { // 內(nèi)層函數(shù)定義
        var inner_var = 3;
        console.log(outer_var + midd_var + inner_var);
        function core_fun() { // 核層函數(shù)定義
            var core_var = 4;
            console.log(outer_var + midd_var + inner_var + core_var);
        };
        core_fun(); // 核層函數(shù)調(diào)用
    };
    inner_fun(); // 內(nèi)層函數(shù)調(diào)用
};
outer_fun(); // 外層函數(shù)調(diào)用

這個(gè)例子簡單來說药版,就是說明了JS函數(shù)可以嵌套定義辑舷,并且內(nèi)層可以訪問外層的變量(包括父節(jié)點(diǎn)和祖先節(jié)點(diǎn))。

4. 閉包

封裝

有了前面這么多鋪墊后槽片,我們可以講解JS的一個(gè)比較費(fèi)解的概念閉包何缓。
假設(shè)我們要實(shí)現(xiàn)一個(gè)SDK,它用來登陸一個(gè)網(wǎng)站还栓,它需要給各個(gè)子系統(tǒng)共享碌廓,以便減少大家的重復(fù)開發(fā)。但是需要考慮避免全局變量等的沖突剩盒。我們期望的使用代碼是這樣的:

var passed = PassportSDK.login('guest', 'guest');
console.log('登陸結(jié)果:' + passed); // 輸出 false

passed = PassportSDK.login('admin', '123456');
console.log('登陸結(jié)果:' + passed); // 輸出 true
console.log('失敗次數(shù):' + PassportSDK.getFailCount()); // 輸出 1 (因?yàn)樵袷∵^一次)
console.log('已經(jīng)登錄谷婆?:' + PassportSDK.isLogined()); // 輸出 true (當(dāng)前是登陸狀態(tài))
console.log(PassportSDK.dbPwd); // 報(bào)錯(cuò),試圖訪問私有成員

那么實(shí)現(xiàn)代碼是:

var PassportSDK = (function() { // 匿名函數(shù)

    // 扮演私有成員:函數(shù)的內(nèi)部變量
    var dbUser = 'admin'; // 數(shù)據(jù)庫賬號(hào)
    var dbPwd = '123456'; // 數(shù)據(jù)庫密碼

    var failCnt = 0; // 存儲(chǔ)失敗次數(shù)
    var status = false; // 記錄登陸狀態(tài)

    // 扮演公開方法:函數(shù)的返回值
    // 返回值是一個(gè)對象辽聊,對象的有些屬性是一個(gè)函數(shù)
    return {

        login: function(user, password) {
            var isPassed = (dbUser == user && dbPwd === password);
            if (! isPassed) {
                failCnt ++;
            } else {
                status = true;
            }
            return isPassed;
        },

        getFailCount: function() {
            return failCnt;
        },

        isLogined: function() {
            return status;
        }

    };

})(); // 匿名函數(shù)的立即調(diào)用

怎么解釋下這段代碼呢纪挎?有了前面的鋪墊,我們可以這么解釋:

  • 閉包是匿名函數(shù)的立即調(diào)用跟匆;
  • 調(diào)用的返回值是一個(gè)對象异袄;
  • 對象的某些屬性是一個(gè)函數(shù)。

如果您是javascript新手贾铝,請反復(fù)琢磨這句話隙轻,并用這句話解讀PassportSDK代碼,您一定會(huì)有所悟垢揩。

閉包有什么好處呢玖绿?

  • 名字空間:它好比Java的package機(jī)制。比如PassportSDK.login方法不會(huì)跟其他的login引發(fā)名字沖突叁巨。
  • 私有變量:函數(shù)嵌套中的內(nèi)部變量微渠,按道理內(nèi)部函數(shù)執(zhí)行完后,就會(huì)釋放掉璃谨;但是因?yàn)楹瘮?shù)把內(nèi)部變量植入到另一個(gè)對象芽狗,當(dāng)做返回值返出去了,導(dǎo)致一方面不能直接訪問(發(fā)揮私有成員的效果了)庶橱,另一方面變量還沒有釋放(因?yàn)楸话鳛榉祷刂盗耍?/li>

繼承

剛才的閉包實(shí)現(xiàn)了名字空間贮勃,還實(shí)現(xiàn)了私有成員。但是Java的面向?qū)ο筮€有繼承苏章,假如我們要在開閉原則下寂嘉,拓展PassportSDK奏瞬,加入一個(gè)logout方法呢?期望使用:

PassportSDKExt.login('admin', '123456'); // 復(fù)用 PassportSDK 的實(shí)現(xiàn)
PassportSDKExt.logout(); // 增加新的功能

實(shí)現(xiàn)代碼如下:

var PassportSDKExt = (function(parent) {

    parent.logout = function() { // 對象可以動(dòng)態(tài)追加屬性
        parent.status = false; // 訪問不了私有成員泉孩,沒有Java的Protected概念
    };

    return parent;

})(PassportSDK); // PassportSDK 是之前的變量

上述代碼就是直接利用了對象可以動(dòng)態(tài)可以追加屬性的機(jī)制硼端。當(dāng)然同樣是用了匿名函數(shù)的立即調(diào)用,只不過這個(gè)匿名函數(shù)有一個(gè)輸入?yún)?shù)寓搬,所以立即調(diào)用時(shí)珍昨,需要把PassportSDK當(dāng)做輸入?yún)?shù)傳遞進(jìn)去。

但是這個(gè)代碼實(shí)際上無法運(yùn)行句喷,因?yàn)?code>parent.status是訪問不了的镣典,它沒有Java的protected成員的概念(子類可以訪問)。

5. 面向?qū)ο?/h2>

模板與實(shí)例

上面的方式我們都采用了 匿名函數(shù)的立即調(diào)用 這種形式脏嚷。但這種形式有個(gè)問題骆撇,只能在定義的時(shí)候調(diào)用一次,后面再想調(diào)用就調(diào)用不了父叙。有點(diǎn)類似Java設(shè)計(jì)模式里面的單例模式神郊。但面向?qū)ο螅瑧?yīng)該有這種模板機(jī)制趾唱,每次調(diào)用構(gòu)造方法都能構(gòu)建一個(gè)新的對象涌乳。為了能多次調(diào)用,我們可以把匿名函數(shù)改成命名函數(shù)甜癞。本實(shí)例完整代碼見 C5Session.js

在改成命名函數(shù)前夕晓,為了更加符合RESTful風(fēng)格,我們把上面PassportSDK的名字重構(gòu)一下悠咱,loginlogout分別重構(gòu)為session.createsession.remove蒸辆,因?yàn)閺馁Y源與資源操作的角度看,登陸的本質(zhì)就是在服務(wù)端分配了一個(gè)session資源析既,登出就是回收session資源躬贡。

  • 名稱重構(gòu)

重構(gòu)后的 匿名函數(shù)的立即調(diào)用 形式:

var session = (function () { // 匿名函數(shù)的立即調(diào)用
    var status = false;  // 私有成員
    return {
        create: function (name, pwd) { // 公有方法
            status = ('admin' == name && '123456' == pwd);
            return status;
        },
        remove: function () { // 公有方法
            status = false;
        },
        isActive: function () { // 公有方法
            return status;
        }
    };
})();

session.create('guest', 'guest'); // 使用
  • 改成命名函數(shù)
var Session = function () { // 命名函數(shù)
    var status = false;  // 私有成員
    return {
        create: function (name, pwd) { // 公有方法
            status = ('admin' == name && '123456' == pwd);
            return status;
        },
        remove: function () { // 公有方法
            status = false;
        },
        isActive: function () { // 公有方法
            return status;
        }
    };
};

var session = Session(); // 命名函數(shù)的調(diào)用
session.create('guest', 'guest'); // 使用

改成命名函數(shù)后,如果想“構(gòu)造一個(gè)對象”眼坏,可以執(zhí)行var session = Session();拂玻,但這種風(fēng)格總跟Java有點(diǎn)不習(xí)慣,Java需要一個(gè)new關(guān)鍵字宰译,比如:Session session = new Session();檐蚜。但是Java領(lǐng)域,F(xiàn)actory模式都不太主張?jiān)谑褂梅街苯佑?code>new了沿侈,因?yàn)槟菢邮菑?qiáng)耦合闯第,設(shè)計(jì)模式里倡導(dǎo)的風(fēng)格是Session session = sessionFactory.newInstance();,而且最近幾年開始流行Vertx vertx = Vertx.vertx()這種風(fēng)格(Java的一個(gè)叫vertx.io的框架中有很多這種風(fēng)格)缀拭。Vertx.vertx() 這種風(fēng)格咳短,如果換到j(luò)avascript里面肃廓,也是非常容易實(shí)現(xiàn)的,它就是Vertx對象诲泌,有個(gè)叫vertx的屬性,這個(gè)屬性是一個(gè)函數(shù)铣鹏。于是我們再改寫一下:

  • Vertx.vertx()風(fēng)格
var Session = {
  session: ${上面的命名函數(shù)}
};

var session = Session.session();
session.create('guest', 'guest');

完整的代碼:

var Session = { // Vertx.vertx() 風(fēng)格
  session: function () { // ${上面的命名函數(shù)}
      var status = false;  // 私有成員
      return {
          create: function (name, pwd) { // 公有方法
              status = ('admin' == name && '123456' == pwd);
              return status;
          },
          remove: function () { // 公有方法
              status = false;
          },
          isActive: function () { // 公有方法
              return status;
          }
      };
  };
};

var session = Session.session();
session.create('guest', 'guest');

到這敷扫,模板就是Session,構(gòu)造實(shí)例就是Session.session()诚卸,跟Java就非常吻合了葵第。利用閉包,也有了類似status屬性的私有成員合溺;通過返回對象卒密,對象的屬性是一個(gè)函數(shù)的形式,有了公有方法棠赛。

靜態(tài)成員與靜態(tài)方法

如果我們把上面的Session比作是Java的類哮奇,那么Session.session()就好比是靜態(tài)方法。如果我們需要一個(gè)靜態(tài)成員睛约,來記錄實(shí)例的個(gè)數(shù)呢鼎俘?

var Session = {
  instanceCount: 0, // 類的靜態(tài)成員 (無法私有化)
  session: function () { // 類的靜態(tài)方法
      Session.instanceCount ++; // 調(diào)用Session.session() 后,實(shí)例數(shù)+1
      var status = false;  // 實(shí)例的私有成員
      return {
          create: function (name, pwd) { // 實(shí)例的公有方法
              status = ('admin' == name && '123456' == pwd);
              return status;
          }
      };
  },
  getInstanceCount: function() { // 類的靜態(tài)方法
    return Session.instanceCount; // 只是對標(biāo)Java風(fēng)格辩涝,實(shí)際instanceCount并非私有
  }
};

var session = Session.session();
Session.getInstanceCount();

類的繼承

類的繼承依然可以用前面講過的方式:

var SessionSub = {
    sessionSub: (function(parent) { // 這段代碼前面講過
        parent.currentUser = function() {
          return '{}';
        }
        return parent;
    })(Session); // 把超類作為參數(shù)
};

SessionSub sessionSub = SessionSub.sessionSub();
sessionSub.currentUser();

6. 包命名空間

前面我們講閉包的時(shí)候贸伐,提到了它類似Java的package機(jī)制。現(xiàn)在我們把前面的閉包和這里的面向?qū)ο螅▽?shí)際上這里面也是有閉包怔揩,只是我們前面自己把閉包的定義弄成了以匿名函數(shù)的立即調(diào)用為前提捉邢,主要是為了初級(jí)接觸時(shí),能記住和理解商膊,對概念咬文嚼字伏伐,筆者認(rèn)為沒有太必要,畢竟使用javascript是一種技能翘狱,并非一種理論)組合起來運(yùn)用秘案,詳細(xì)代碼見C6PackageSession.js

以下我們定義一個(gè)package潦匈,名字叫io_example_passport阱高,它下面有兩個(gè)類:SessionUser

var io_example_passport = (function () { // 依然是匿名函數(shù)的立即調(diào)用
    return {
        Session: { // 類似Java的類
            instanceCount: 0, // 類的靜態(tài)成員 (無法私有化)
            session: function () { // 類的靜態(tài)方法
                Session.instanceCount++; // 調(diào)用Session.session() 后,實(shí)例數(shù)+1
                var status = false;  // 實(shí)例的私有成員
                return {
                    create: function (name, pwd) { // 實(shí)例的公有方法
                        status = ('admin' == name && '123456' == pwd);
                        return status;
                    },
                    isActive: function() {
                        return status;
                    }
                };

            },

            getInstanceCount: function () { // 類的靜態(tài)方法
                return Session.instanceCount; // 只是對標(biāo)Java風(fēng)格茬缩,實(shí)際instanceCount并非私有
            }
        },

        User: { // 類似Java的類
            user: function(session) { // 帶參數(shù)的構(gòu)造函數(shù)
                var priSession = session; // 私有成員
                var user = {};
                user.getUser = function () {
                    if (priSession.isActive) {
                        return "{'name': 'zhangsan', 'uid': 514233}";
                    } else {
                        throw 'Please login first';
                    }
                };
                user.getSession = function () {
                    return priSession;
                };
                return user;
            }
        }
    }

})();

如何使用上面的代碼呢赤惊?跟Java還是比較相似的:

// Session類
var Session = io_example_passport.Session;
var session1 = Session.session();
var session2 = Session.session();
console.log('實(shí)例個(gè)數(shù):' + Session.getInstanceCount());
var s1Passed = session1.create('guest', 'guest');
console.log('s1 登陸結(jié)果:' + s1Passed);
var s2Passed = session2.create('admin', '123456');
console.log('s2 登陸結(jié)果:' + s2Passed);
console.log('s2 登陸狀態(tài):' + session2.isActive());


// User 類
var User = io_example_passport.User;
var user = User.user(session2);
var currentUser = user.getUser();
console.log('current user on session2: ' + currentUser);
console.log('當(dāng)前用戶依然保持登陸嗎?' + user.getSession().isActive());

運(yùn)行結(jié)果:

$ node C6PackageSession.js
實(shí)例個(gè)數(shù):2
s1 登陸結(jié)果:false
s2 登陸結(jié)果:true
s2 登陸狀態(tài):true
current user on session2: {'name': 'zhangsan', 'uid': 514233}
當(dāng)前用戶依然保持登陸嗎凰锡?true

雖然這里是通過node C6PackageSession.js來運(yùn)行的未舟,其實(shí)您也可以通過瀏覽器運(yùn)行圈暗,用瀏覽器直接打開C6PackageSession.html即可。

C6PackageSession.html

7. 回歸總結(jié)

本文重點(diǎn)講解了javascript的函數(shù)與對象的特點(diǎn)裕膀,并由它們的相互糾纏(即:返回對象的函數(shù)员串,返回函數(shù)的函數(shù),對象屬性是一個(gè)函數(shù))引出了閉包的概念昼扛。我們把閉包不嚴(yán)謹(jǐn)?shù)亩x為:匿名函數(shù)的立即調(diào)用寸齐;調(diào)用返回的是一個(gè)對象;對象的某些屬性是一個(gè)函數(shù)抄谐。渺鹦,然后再由匿名函數(shù)的一次調(diào)用的缺陷,重構(gòu)成命名函數(shù)蛹含,Vertx.vertx()風(fēng)格毅厚,從而實(shí)現(xiàn)了類似Java的面向?qū)ο蟆W詈笃窒洌验]包和面向?qū)ο笤俳M合了一次吸耿,引入了package命名空間機(jī)制。

當(dāng)然javascript是發(fā)展的憎茂,關(guān)于模塊化與面向?qū)ο蟮臋C(jī)制珍语,還有prototype機(jī)制,還有ES6直接從語言層面引入了類機(jī)制竖幔。另外一個(gè)是板乙,借助webpack這種工具,可以更好的模塊化拳氢,然后“編譯”生成一個(gè)單一的js文件募逞。您完全沒有聽錯(cuò),當(dāng)項(xiàng)目規(guī)模大的時(shí)候馋评,為了更好的模塊化放接,即便是javascript動(dòng)態(tài)腳本語言,都需要類似Java語言一樣去“編譯”(可能的工作包括把多個(gè)JS文件合并成一個(gè)JS文件留特;也可以用類JS語言寫纠脾,比如CoffeJS,然后翻譯成純JS蜕青;還可以壓縮最小化等)苟蹈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市右核,隨后出現(xiàn)的幾起案子慧脱,更是在濱河造成了極大的恐慌,老刑警劉巖贺喝,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菱鸥,死亡現(xiàn)場離奇詭異宗兼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)氮采,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門殷绍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鹊漠,你說我怎么就攤上這事篡帕。” “怎么了贸呢?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拢军。 經(jīng)常有香客問我楞陷,道長,這世上最難降的妖魔是什么茉唉? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任固蛾,我火速辦了婚禮,結(jié)果婚禮上度陆,老公的妹妹穿的比我還像新娘艾凯。我一直安慰自己,他們只是感情好懂傀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布趾诗。 她就那樣靜靜地躺著,像睡著了一般蹬蚁。 火紅的嫁衣襯著肌膚如雪恃泪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天犀斋,我揣著相機(jī)與錄音贝乎,去河邊找鬼。 笑死叽粹,一個(gè)胖子當(dāng)著我的面吹牛览效,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虫几,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锤灿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了持钉?” 一聲冷哼從身側(cè)響起衡招,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎每强,沒想到半個(gè)月后始腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體州刽,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年浪箭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穗椅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奶栖,死狀恐怖匹表,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宣鄙,我是刑警寧澤袍镀,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站冻晤,受9級(jí)特大地震影響苇羡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鼻弧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一设江、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧攘轩,春花似錦叉存、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至笨篷,卻和暖如春甫菠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冕屯。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工寂诱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人安聘。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓痰洒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浴韭。 傳聞我的和親對象是個(gè)殘疾皇子丘喻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法念颈,內(nèi)部類的語法泉粉,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,632評(píng)論 18 399
  • 從三月份找實(shí)習(xí)到現(xiàn)在嗡靡,面了一些公司跺撼,掛了不少,但最終還是拿到小米讨彼、百度歉井、阿里、京東哈误、新浪哩至、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,248評(píng)論 11 349
  • 余老師領(lǐng)讀《大學(xué)》蜜自,第一講原文解釋菩貌,第二講理解基礎(chǔ)上提出問題。我做了如下思考: 1.小李在單位跟同事相處不好重荠,該怎...
    一說就笑閱讀 245評(píng)論 1 1
  • 不知何時(shí)起菜谣,懷舊之風(fēng)似乎日漸盛行。懷念兒時(shí)風(fēng)物晚缩,追憶悠游時(shí)光.照片里的故事,泛黃的老照片,回憶曾經(jīng)的往昔媳危。朋友圈...
    yanqiong閱讀 406評(píng)論 0 0
  • 條件編譯 1荞彼、#if, #elif待笑, #else鸣皂, #endif#if條件1代碼段1#elif 條件2代碼段2.....
    秋燈鎖憶閱讀 102評(píng)論 0 0