<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

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

        來源:懂視網 責編:小采 時間:2020-11-27 21:12:09
        文檔

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

        JavaScript的模塊化:封裝(閉包),繼承(原型)介紹_javascript技巧:雖然 JavaScript 天生就是一副隨隨便便的樣子,但是隨著瀏覽器能夠完成的事情越來越多,這門語言也也越來越經常地擺出正襟危坐的架勢。在復雜的邏輯下, JavaScript 需要被模塊化,模塊需要封裝起來,只留下供外界調用的接口。閉包是 JavaScript
        推薦度:
        導讀JavaScript的模塊化:封裝(閉包),繼承(原型)介紹_javascript技巧:雖然 JavaScript 天生就是一副隨隨便便的樣子,但是隨著瀏覽器能夠完成的事情越來越多,這門語言也也越來越經常地擺出正襟危坐的架勢。在復雜的邏輯下, JavaScript 需要被模塊化,模塊需要封裝起來,只留下供外界調用的接口。閉包是 JavaScript

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

        我們試圖在頁面上維護一個計數器對象 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 對象,也就充當了類的作用。

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

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

        JavaScript的模塊化:封裝(閉包),繼承(原型)介紹_javascript技巧:雖然 JavaScript 天生就是一副隨隨便便的樣子,但是隨著瀏覽器能夠完成的事情越來越多,這門語言也也越來越經常地擺出正襟危坐的架勢。在復雜的邏輯下, JavaScript 需要被模塊化,模塊需要封裝起來,只留下供外界調用的接口。閉包是 JavaScript
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲国产成人a精品不卡在线| 国产啪精品视频网免费| 亚洲女人被黑人巨大进入| 亚洲精品无码一区二区| 久久久久久99av无码免费网站| 亚洲国语在线视频手机在线| 国产精品爱啪在线线免费观看| 亚洲美女视频一区| 四虎1515hh永久久免费| 亚洲日本乱码卡2卡3卡新区| 一二三四影视在线看片免费 | 亚洲精品在线视频观看| 久久一区二区三区免费播放 | 99精品视频在线观看免费播放 | 亚洲日韩国产欧美一区二区三区 | 亚洲一区在线观看视频| 性感美女视频在线观看免费精品| 亚洲一区欧洲一区| 免费被黄网站在观看| 污污污视频在线免费观看| 国产成人无码综合亚洲日韩| 一级毛片免费观看| 亚洲中文字幕一二三四区苍井空| 天天操夜夜操免费视频| 一级成人生活片免费看| 亚洲高清专区日韩精品| 91精品成人免费国产片| 亚洲AV无码一区二区一二区| 免费一级特黄特色大片在线 | 免费在线不卡视频| 野花香在线视频免费观看大全| 亚洲精品视频久久| 日本不卡高清中文字幕免费| 9久热精品免费观看视频| 亚洲大香伊人蕉在人依线| 国产成人免费全部网站| 在线观看特色大片免费网站| 亚洲另类自拍丝袜第1页| 免费成人在线观看| 亚洲黄色片免费看| 一级毛片不卡免费看老司机|