雖然 JavaScript 天生就是一副隨便的樣子,但是隨著瀏覽器能夠完成的事情越來越多,這門語言也越來越經常地擺出正襟危坐的架勢。在複雜的邏輯下, JavaScript 需要被模組化,模組需要封裝起來,只留下供外界呼叫的介面。閉包是 JavaScript 中實作模組封裝的關鍵,也是許多初學者難以理解的要點。最初,我也陷入迷惑之中。現在,我自信對這個概念已經有了比較深入的理解。為了便於理解,文中試圖封裝一個比較簡單的物件。
我們試著在頁面上維護一個計數器物件 ticker ,這個物件維護一個數值 n 。隨著使用者的操作,我們可以增加一次計數(將數值 n 加上 1 ),但不能減少 n 或直接改變 n 。而且,我們需要時不時查詢這個數值。
門戶大開的 JSON 風格模組化
一個入口網站大開的方式是:
程式碼如下:
var ticker = {
n:0,
>
這種方式書寫自然,而且確實有效,我們需要增加一次計數時,就調用 ticker.tick() 方法,需要查詢次數時,就訪問 ticker.n 變數。但其缺點也是顯而易見的:模組的使用者被允許自由地改變 n ,例如呼叫 ticker.n-- 或 ticker.n=-1 。我們並沒有對 ticker 進行封裝, n 和 tick() 看上去是 ticker 的“成員”,但是它們的可訪問性和 ticker 一樣,都是全局性的(如果 ticker 是全局變量的話)。在封裝性上,這種模組化的方式比下面這種更可笑的方式,只好那麼一點點(雖然對有些簡單的應用來說,這一點點也足夠了)。程式碼如下:
var ticker = {};
var ticker = {};
; 0;
var tickerTick = function(){
tickerTick();
程式碼如下:
var func = ticker.tick;
; );
程式碼如下:
}
複製程式碼
程式碼如下:
function tipconfig){
function tipconfig){
你或許會疑惑,怎麼 ticker 從物件變成函數了?這是因為 JavaScript 中只有函數具有作用域,從函數體外無法存取函數內部的變數。 ticker() 外訪問 ticker.n 獲得 undefined ,而 tick() 內訪問 n 卻沒有問題。從 tick() 到 ticker() 再到全域,這就是 JavaScript 中的「作用域鏈」。
可是還有問題,那就是--怎麼呼叫 tick() ? ticker() 的作用域將 tick() 也掩蓋了起來。解決方法有兩種:
•1)將需要呼叫方法作為傳回值,正如我們將遞增n 的方法作為ticker() 的回傳值;
•2)設定外層作用域的變量,正如我們在ticker()中設定getN 。
var getN;
var n = config.nStart;
getN = function(){
return n; };
}
var tick = ticker({nStart:100,step:2});
tick();
console.log(getN()); // ->102
請看,這時,變數 n 就處在「閉包」之中,在 ticker() 外部無法直接存取它,但是卻可以透過兩個方法來觀察或操縱它。
可是,我還是覺得不大對勁?如果我需要維持兩個具有相同功能的物件 ticker1 和 ticker2 ,那該怎麼辦? ticker() 只有一個,總不能再寫一次吧?
new 運算子與建構子
如果透過 new 運算子呼叫一個函數,就會建立一個新的對象,並使用該物件呼叫這個函數。在我的理解中,下面的程式碼中 t1 和 t2 的構造過程是一樣的。
複製程式碼
t2.func();
t2.func = undefined;
t1 和 t2 都是新建構的對象, myClass() 就是建構子了。類似的, ticker() 可以重新寫成。
function TICKER(config){
config.nStart;
this.getN = function(){
return 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.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){
function TICKER(config){
config.nStart;
}
TICKER.prototype.getN = function{
// attention : invalid implementation
return n;
}; // attention : invalid implementation
n = config.step;
請注意,這是無效的實作。因為原型物件的方法不能存取閉包中的內容,也就是變數 n 。 TICK() 方法運作之後無法再存取 n ,瀏覽器會將 n 銷毀。為了存取閉包中的內容,物件必須有一些簡潔的依賴實例的方法,來存取閉包中的內容,然後在其 prototype 上定義複雜的公有方法來實現邏輯。實際上,例子中的 tick() 方法就已經夠簡潔了,我們還是把它放回 TICKER 中吧。下面實作一個複雜些的方法 tickTimes() ,它將允許呼叫者指定呼叫 tick() 的次數。
function TICKER(config){
function TICKER(config){
config.nStart;
this.getN = function(){
return n;
}; } ;
}
TICKER.prototype.tickTimes = function(n){
while(n>0){
🎜>};
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

JavaScript核心數據類型在瀏覽器和Node.js中一致,但處理方式和額外類型有所不同。 1)全局對像在瀏覽器中為window,在Node.js中為global。 2)Node.js獨有Buffer對象,用於處理二進制數據。 3)性能和時間處理在兩者間也有差異,需根據環境調整代碼。

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

禪工作室 13.0.1
強大的PHP整合開發環境

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具