亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript的模塊化:封裝(閉包),繼承(原型) 介紹

2019-11-20 22:30:08
字體:
來源:轉載
供稿:網友

雖然 JavaScript 天生就是一副隨隨便便的樣子,但是隨著瀏覽器能夠完成的事情越來越多,這門語言也也越來越經常地擺出正襟危坐的架勢。在復雜的邏輯下, JavaScript 需要被模塊化,模塊需要封裝起來,只留下供外界調用的接口。閉包是 JavaScript 中實現模塊封裝的關鍵,也是很多初學者難以理解的要點。最初,我也陷入迷惑之中?,F在,我自信對這個概念已經有了比較深入的理解。為了便于理解,文中試圖封裝一個比較簡單的對象。

我們試圖在頁面上維護一個計數器對象 ticker ,這個對象維護一個數值 n 。隨著用戶的操作,我們可以增加一次計數(將數值 n 加上 1 ),但不能減少 n 或直接改變 n 。而且,我們需要時不時查詢這個數值。

門戶大開的 JSON 風格模塊化

一種門戶大開的方式是:

復制代碼 代碼如下:

var ticker = {
    n:0,
    tick:function(){
        this.n++;
    },
};

這種方式書寫自然,而且確實有效,我們需要增加一次計數時,就調用 ticker.tick() 方法,需要查詢次數時,就訪問 ticker.n 變量。但是其缺點也是顯而易見的:模塊的使用者被允許自由地改變 n ,比如調用 ticker.n-- 或者 ticker.n=-1 。我們并沒有對 ticker 進行封裝, n 和 tick() 看上去是 ticker 的“成員”,但是它們的可訪問性和 ticker 一樣,都是全局性的(如果 ticker 是全局變量的話)。在封裝性上,這種模塊化的方式比下面這種更加可笑的方式,只好那么一點點(雖然對有些簡單的應用來說,這一點點也足夠了)。

復制代碼 代碼如下:

var ticker = {};
var tickerN = 0;
var tickerTick = function(){
    tickerN++;
}

tickerTick();

值得注意的是,在 tick() 中,我訪問的是 this.n ――這并不是因為 n 是 ticker 的成員,而是因為調用 tick() 的是 ticker 。事實上這里寫成 ticker.n 會更好,因為如果調用 tick() 的不是 ticker ,而是其他什么東西,比如:

復制代碼 代碼如下:

var func = ticker.tick;
func();

這時,調用 tick() 的其實是 window ,而函數執行時會試圖訪問 window.n 而出錯。

事實上,這種“門戶大開”型的模塊化方式,往往用來組織 JSON 風格的數據,而不是程序。比如,我們可以將下面這個 JSON 對象傳給 ticker 的某個函數,來確定 ticker 從 100 開始計數,每次遞進 2 。

復制代碼 代碼如下:

var config = {
    nStart:100,
    step:2
}

作用域鏈和閉包
來看下面的代碼,注意我們已經實現了傳入 config 對 ticker 進行自定義。

復制代碼 代碼如下:

function ticker(config){
    var n = config.nStart;
    function tick(){
        n += config.step;
    }
}
console.log(ticker.n); // ->undefined

你也許會疑惑,怎么 ticker 從對象變成了函數了?這是因為 JavaScript 中只有函數具有作用域,從函數體外無法訪問函數內部的變量。 ticker() 外訪問 ticker.n 獲得 undefined ,而 tick() 內訪問 n 卻沒有問題。從 tick() 到 ticker() 再到全局,這就是 JavaScript 中的“作用域鏈”。

可是還有問題,那就是――怎么調用 tick() ? ticker() 的作用域將 tick() 也掩蓋了起來。解決方法有兩種:

•1)將需要調用方法作為返回值,正如我們將遞增 n 的方法作為 ticker() 的返回值;
•2)設定外層作用域的變量,正如我們在 ticker() 中設置 getN 。

復制代碼 代碼如下:

var getN;
function ticker(config){
    var n = config.nStart;
    getN = function(){
        return n;
    };
    return function(){
        n += config.step;
    };
}

var tick = ticker({nStart:100,step:2});
tick();
console.log(getN()); // ->102

請看,這時,變量 n 就處在“閉包”之中,在 ticker() 外部無法直接訪問它,但是卻可以通過兩個方法來觀察或操縱它。

在本節第一段代碼中, ticker() 方法執行之后, n 和 tick() 就被銷毀了,直到下一次調用該函數時再創建;但是在第二段代碼中, ticker() 執行之后, n 不會被銷毀,因為 tick() 和 getN() 可能訪問它或改變它,瀏覽器會負責維持n。我對“閉包”的理解就是:用以保證 n 這種處在函數作用域內,函數執行結束后仍需維持,可能被通過其他方式訪問的變量 不被銷毀的機制。

可是,我還是覺得不大對勁?如果我需要維持兩個具有相同功能的對象 ticker1 和 ticker2 ,那該怎么辦? ticker() 只有一個,總不能再寫一遍吧?

new 運算符與構造函數
如果通過 new 運算符調用一個函數,就會創建一個新的對象,并使用該對象調用這個函數。在我的理解中,下面的代碼中 t1 和 t2 的構造過程是一樣的。

復制代碼 代碼如下:

function myClass(){}
var t1 = new myClass();
var t2 = {};
t2.func = myClass;
t2.func();
t2.func = undefined;

t1 和 t2 都是新構造的對象, myClass() 就是構造函數了。類似的, ticker() 可以重新寫成。

復制代碼 代碼如下:

function TICKER(config){
    var n = config.nStart;
    this.getN = function(){
        return n;
    };
    this.tick = function(){
        n += config.step;
    }
}

var ticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // ->102
var ticker2 = new TICKER({nStart:20,step:3});
ticker2.tick();
ticker2.tick();
console.log(ticker2.getN()); // ->26

習慣上,構造函數采用大寫。注意, TICKER() 仍然是個函數,而不是個純粹的對象(之所以說“純粹”,是因為函數實際上也是對象, TICKER() 是函數對象),閉包依舊有效,我們無法訪問 ticker1.n 。

原型 prototype 與繼承
上面這個 TICKER() 還是有缺陷,那就是, ticker1.tick() 和 ticker2.tick() 是互相獨立的!請看,每使用 new 運算符調用 TICKER() ,就會生成一個新的對象并生成一個新的函數綁定在這個新的對象上,每構造一個新的對象,瀏覽器就要開辟一塊空間,存儲 tick() 本身和 tick() 中的變量,這不是我們所期望的。我們期望 ticker1.tick 和 ticker2.tick 指向同一個函數對象。

這就需要引入原型。

JavaScript 中,除了 Object 對象,其他對象都有一個 prototype 屬性,這個屬性指向另一個對象。這“另一個對象”依舊有其原型對象,并形成原型鏈,最終指向 Object 對象。在某個對象上調用某方法時,如果發現這個對象沒有指定的方法,那就在原型鏈上一次查找這個方法,直到 Object 對象。

函數也是對象,因此函數也有原型對象。當一個函數被聲明出來時(也就是當函數對象被定義出來時),就會生成一個新的對象,這個對象的 prototype 屬性指向 Object 對象,而且這個對象的 constructor 屬性指向函數對象。

通過構造函數構造出的新對象,其原型指向構造函數的原型對象。所以我們可以在構造函數的原型對象上添加函數,這些函數就不是依賴于 ticker1 或 ticker2 ,而是依賴于 TICKER 了。

你也許會這樣做:

復制代碼 代碼如下:

function TICKER(config){
    var n = config.nStart;
}
TICKER.prototype.getN = function{
    // attention : invalid implementation
    return n;
};
TICKER.prototype.tick = function{
    // attention : invalid implementation
    n += config.step;
};

請注意,這是無效的實現。因為原型對象的方法不能訪問閉包中的內容,也就是變量 n 。 TICK() 方法運行之后無法再訪問到 n ,瀏覽器會將 n 銷毀。為了訪問閉包中的內容,對象必須有一些簡潔的依賴于實例的方法,來訪問閉包中的內容,然后在其 prototype 上定義復雜的公有方法來實現邏輯。實際上,例子中的 tick() 方法就已經足夠簡潔了,我們還是把它放回到 TICKER 中吧。下面實現一個復雜些的方法 tickTimes() ,它將允許調用者指定調用 tick() 的次數。

復制代碼 代碼如下:

function TICKER(config){
    var n = config.nStart;
    this.getN = function(){
        return n;
    };
    this.tick = function(){
        n += config.step;
    };
}
TICKER.prototype.tickTimes = function(n){
    while(n>0){
        this.tick();
        n--;
    }
};
var ticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // ->102
var ticker2 = new TICKER({nStart:20,step:3});
ticker2.tickTimes(2);
console.log(ticker2.getN()); // ->26

這個 TICKER 就很好了。它封裝了 n ,從對象外部無法直接改變它,而復雜的函數 tickTimes() 被定義在原型上,這個函數通過調用實例的小函數來操作對象中的數據。

所以,為了維持對象的封裝性,我的建議是,將對數據的操作解耦為盡可能小的單元函數,在構造函數中定義為依賴于實例的(很多地方也稱之為“私有”的),而將復雜的邏輯實現在原型上(即“公有”的)。

最后再說一些關于繼承的話。實際上,當我們在原型上定義函數時,我們就已經用到了繼承! JavaScript 中的繼承比 C++ 中的更……呃……簡單,或者說簡陋。在 C++ 中,我們可能會定義一個 animal 類表示動物,然后再定義 bird 類繼承 animal 類表示鳥類,但我想討論的不是這樣的繼承(雖然這樣的繼承在 JavaScript 中也可以實現);我想討論的繼承在 C++ 中將是,定義一個 animal 類,然后實例化了一個 myAnimal 對象。對,這在 C++ 里就是實例化,但在 JavaScript 中是作為繼承來對待的。

JavaScript 并不支持類,瀏覽器只管當前有哪些對象,而不會額外費心思地去管,這些對象是什么 class 的,應該具有怎樣的結構。在我們的例子中, TICKER() 是個函數對象,我們可以對其賦值(TICKER=1),將其刪掉(TICKER=undefined),但是正因為當前有 ticker1 和 ticker2 兩個對象是通過 new 運算符調用它而來的, TICKER() 就充當了構造函數的作用,而 TICKER.prototype 對象,也就充當了類的作用。

以上就是我所了解的 JavaScript 模塊化的方法,如果您也是初學者,希望能對您有所幫助。如果有不對的地方,也勞駕您指出。

作者:一葉齋主人
出處:www.cnblogs.com/yiyezhai

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美大秀在线观看| 国自在线精品视频| 亚洲成人黄色在线| 亚洲欧美精品中文字幕在线| 亚洲色图欧美制服丝袜另类第一页| 日韩国产欧美精品一区二区三区| 隔壁老王国产在线精品| 热久久99这里有精品| 福利一区视频在线观看| 欧美情侣性视频| 欧美激情精品久久久久久蜜臀| 亚洲xxxxx| 91po在线观看91精品国产性色| 尤物九九久久国产精品的特点| 成人h猎奇视频网站| 亚洲免费人成在线视频观看| 欧美日韩国产精品一区| 色悠久久久久综合先锋影音下载| 中国日韩欧美久久久久久久久| 81精品国产乱码久久久久久| 亚洲日本aⅴ片在线观看香蕉| 国产精自产拍久久久久久| 日韩中文字幕在线视频| 国产噜噜噜噜久久久久久久久| 亚洲国产精品久久久久秋霞蜜臀| 国产一区二区三区免费视频| 91精品国产99| 欧美激情一二三| 欧美国产视频一区二区| 久久不射热爱视频精品| 亚洲欧美一区二区三区情侣bbw| 欧美综合激情网| 日韩免费不卡av| 欧美高清在线视频观看不卡| 日韩高清欧美高清| 欧美尤物巨大精品爽| 国产成人涩涩涩视频在线观看| 欧美精品18videosex性欧美| 日本韩国在线不卡| 久久久久久av| 欧美另类精品xxxx孕妇| 久久人人爽人人爽爽久久| 欧美一级电影在线| 欧美精品少妇videofree| 久久久久久有精品国产| 国产精品中文字幕在线观看| 自拍偷拍亚洲区| 国产精品成人国产乱一区| 久久这里只有精品视频首页| 中文字幕最新精品| 国产91精品在线播放| 国产在线播放91| 亚洲一区二区免费在线| 97婷婷涩涩精品一区| 亚洲xxx视频| 久久精品久久久久电影| 91成人在线视频| 欧日韩在线观看| 亚洲激情成人网| 精品无人区乱码1区2区3区在线| 亚洲国产精品视频在线观看| 国产精品丝袜一区二区三区| 中日韩午夜理伦电影免费| 日韩欧美成人网| 国产午夜精品美女视频明星a级| 国产精品一区二区女厕厕| 亚洲免费av电影| 欧美壮男野外gaytube| 日韩美女视频在线观看| 日韩电影中文字幕av| 91精品91久久久久久| 国产精品精品久久久| 亚洲国产精品99久久| 亚洲欧美精品一区二区| 亚洲xxxx妇黄裸体| 日日狠狠久久偷偷四色综合免费| 欧美日本中文字幕| 亚洲美女喷白浆| 日韩在线中文字幕| 中文字幕精品国产| 精品国产一区久久久| 亚洲黄色www网站| 91色琪琪电影亚洲精品久久| 亚洲欧美一区二区三区情侣bbw| 亚洲欧洲在线看| 国产精品亚洲美女av网站| 91免费国产视频| 亚洲一区中文字幕在线观看| 狠狠躁18三区二区一区| 国产综合久久久久久| 精品视频久久久| 中文字幕欧美在线| 不卡av电影在线观看| 91在线直播亚洲| 国产精品香蕉在线观看| 亚洲图片在线综合| 欧美性受xxxx黑人猛交| 亚洲国产精品一区二区三区| 亚洲激情 国产| 欧美日韩成人黄色| 欧美福利视频网站| 国产视频久久久久| 国产成人+综合亚洲+天堂| 日韩av免费网站| 久久视频这里只有精品| 国产精品99久久久久久久久久久久| 亚洲人成电影网| www.欧美视频| 欧美成人一区二区三区电影| 欧美色播在线播放| 51精品在线观看| 亚洲专区在线视频| 国产中文欧美精品| 国产成人精品免费久久久久| 国产精品久久久久久av福利| 欧美色欧美亚洲高清在线视频| 久久在线免费观看视频| 国产欧美一区二区三区久久人妖| 性欧美亚洲xxxx乳在线观看| 亚洲午夜未删减在线观看| 38少妇精品导航| 欧美日韩免费在线| 欧美日韩国产麻豆| 日本电影亚洲天堂| 国产欧亚日韩视频| 伊人久久五月天| 91av中文字幕| 日本在线观看天堂男亚洲| 大胆人体色综合| 最好看的2019年中文视频| 日韩av中文在线| 色综合天天狠天天透天天伊人| 亚洲永久免费观看| 欧美另类老肥妇| 亚洲福利视频久久| 黄网站色欧美视频| 欧美亚洲另类视频| 亚洲欧美成人一区二区在线电影| 九九热这里只有精品6| 成人国产精品一区| 91在线播放国产| 亚洲高清久久久久久| 亚洲精品美女免费| 欧美激情网友自拍| 欧美中文在线字幕| 久久香蕉频线观| 亚洲日本aⅴ片在线观看香蕉| 中文字幕精品在线| 亚洲国产精品成人va在线观看| 欧美日本高清视频| 欧美激情18p| 日韩成人在线视频观看| 日韩欧美在线视频日韩欧美在线视频| 日韩欧美亚洲国产一区| 热草久综合在线| 亚洲国内高清视频| 色悠悠国产精品| 日韩成人av网址| 久久中文字幕在线| 久久精视频免费在线久久完整在线看| 中文字幕在线观看亚洲| 九九精品在线视频| 国产精品欧美日韩久久| 热久久视久久精品18亚洲精品|