JavaScript,這門賦能萬維網的編程語言,自1995年5月由Brendan Eich創建以來,已成為一種廣泛應用且用途廣泛的技術。儘管它取得了成功,但它也受到了相當多的批評,尤其是一些特性。例如,對像在用作索引時被強制轉換為字符串形式,1 == "1" 返回true,或者臭名昭著的令人困惑的this
關鍵字。然而,一個特別有趣的特性是存在各種實現變量私有性的技術。
目前,JavaScript 中並沒有直接創建私有變量的方法。在其他語言中,您可以使用private
關鍵字或雙下劃線,一切都能正常工作,但在JavaScript 中,變量私有性具有使其更類似於語言的湧現特性而非預期功能的特性。讓我們先介紹一下我們問題的背景。
var
關鍵字
在2015 年之前,基本上只有一種創建變量的方法,那就是var
關鍵字。 var
是函數作用域的,這意味著用該關鍵字實例化的變量只能被函數內的代碼訪問。在函數外部或本質上是“全局”的情況下,該變量將可被定義變量後執行的任何內容訪問。如果您嘗試在同一作用域中訪問變量在其定義之前,您將得到undefined
而不是錯誤。這是由於var
關鍵字的“提升”方式。
// 在全局作用域中定義"a" var a = 123; // 在函數作用域中定義"b" (function() { console.log(b); //=> 由於提升,返回"undefined" 而不是錯誤。 var b = 456; })(); console.log(a); // => 123 console.log(b); // 拋出"ReferenceError" 異常,因為"b" 無法從函數作用域外部訪問。
ES6 變量的誕生
2015 年,ES6/ES2015 正式發布,隨之而來的是兩個新的變量關鍵字: let
和const
。兩者都是塊作用域的,這意味著用這些關鍵字創建的變量可以被同一對括號內的任何內容訪問。與var
相同,但let
和const
變量無法在循環、函數、if 語句、括號等塊作用域之外訪問。
const a = 123; // 塊作用域示例#1 if (true) { const b = 345; } // 塊作用域示例#2 { const c = 678; } console.log(a); // 123 console.log(b); // 拋出"ReferenceError",因為"b" 無法從塊作用域外部訪問。 console.log(c); // 拋出"ReferenceError",因為"b" 無法從塊作用域外部訪問。
由於作用域外部的代碼無法訪問變量,因此我們獲得了私有性的湧現特性。我們將介紹一些以不同方式實現它的技術。
使用函數
由於JavaScript 中的函數也是塊,因此所有變量關鍵字都與它們一起工作。此外,我們可以實現一個非常有用的設計模式,稱為“模塊”。
模塊設計模式
谷歌依靠牛津詞典來定義“模塊”:
程序可以從中構建或可以分析複雜活動的多個不同但相互關聯的單元中的任何一個。
—“模塊”定義1.2
模塊設計模式在JavaScript 中非常有用,因為它結合了公共和私有組件,並且允許我們將程序分解成更小的組件,只通過稱為“封裝”的過程公開另一個程序部分應該能夠訪問的內容。通過這種方法,我們隻公開需要使用的內容,並隱藏不需要看到的內容。我們可以利用函數作用域來實現這一點。
const CarModule = () => { let milesDriven = 0; let speed = 0; const accelerate = (amount) => { speed = amount; milesDriven = speed; } const getMilesDriven = () => milesDriven; // 使用"return" 關鍵字,您可以控制哪些內容被公開,哪些內容被隱藏。在本例中,我們隻公開accelerate() 和getMilesDriven() 函數。 return { accelerate, getMilesDriven } }; const testCarModule = CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
這樣,我們可以獲得行駛里程數以及加速度,但是由於用戶在這種情況下不需要訪問速度,我們可以通過隻公開accelerate()
和getMilesDriven()
方法來隱藏它。本質上, speed
是一個私有變量,因為它只能被同一塊作用域內的代碼訪問。私有變量的好處在這種情況下開始變得清晰。當您刪除訪問變量、函數或任何其他內部組件的能力時,您會減少因其他人錯誤地使用本不應使用的內容而導致錯誤的表面積。
另一種方法
在這個第二個例子中,你會注意到增加了this
關鍵字。 ES6 箭頭函數(=>)和傳統函數(){}之間存在差異。使用function
關鍵字,您可以使用this
,它將綁定到函數本身,而箭頭函數不允許任何類型的this
關鍵字的使用。兩者都是創建模塊的同樣有效的方法。核心思想是公開應該訪問的部分,並保留不應該交互的其他部分,因此既有公共數據也有私有數據。
function CarModule() { let milesDriven = 0; let speed = 0; // 在這種情況下,我們改為使用"this" 關鍵字, // 它指的是CarModule this.accelerate = (amount) => { speed = amount; milesDriven = speed; } this.getMilesDriven = () => milesDriven; } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
ES6 類
類是ES6 中的另一個新增功能。類本質上是語法糖——換句話說,仍然是一個函數,但可能會將其“美化”成更容易表達的形式。對於類,變量私有性(截至目前)幾乎是不可能的,除非對代碼進行一些重大更改。
讓我們來看一個類示例。
class CarModule { /* milesDriven = 0; speed = 0; */ constructor() { this.milesDriven = 0; this.speed = 0; } accelerate(amount) { this.speed = amount; this.milesDriven = this.speed; } getMilesDriven() { return this.milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven());
首先要注意的是, milesDriven
和speed
變量位於constructor()
函數內。請注意,您也可以在構造函數之外定義變量(如代碼註釋所示),但無論如何它們在功能上都是相同的。問題是這些變量將是公共的,並且可以被類外部的元素訪問。
讓我們看看解決這個問題的一些方法。
使用下劃線
在私有性是為了防止協作者犯一些災難性錯誤的情況下,用下劃線(_)作為變量前綴,儘管仍然對外部“可見”,但足以向開發人員發出信號,“不要碰這個變量”。因此,例如,我們現在有以下內容:
// 這是類的新的構造函數。請注意,它也可以表示為構造函數()之外的以下內容。 /* _milesDriven = 0; _speed = 0; */ constructor() { this._milesDriven = 0; this._speed = 0; }
雖然這對於它的特定用例有效,但仍然可以肯定地說,它在許多方面都不理想。您仍然可以訪問變量,但您還必須修改變量名稱。
將所有內容放在構造函數內
從技術上講,確實有一種方法可以在類中使用私有變量,那就是將所有變量和方法放在constructor()
函數內。讓我們來看一下。
class CarModule { constructor() { let milesDriven = 0; let speed = 0; this.accelerate = (amount) => { speed = amount; milesDriven = speed; } this.getMilesDriven = () => milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // undefined -- 我們現在有了真正的變量私有性。
這種方法實現了真正的變量私有性,因為無法直接訪問未故意公開的任何變量。問題是我們現在有了,嗯,與我們之前相比,看起來不太好的代碼,此外它還破壞了我們使用類時語法糖的好處。此時,我們不妨使用function()
方法。
使用WeakMap
還有一種更具創造性的方法來創建私有變量,那就是使用WeakMap()
。雖然它可能聽起來類似於Map
,但兩者非常不同。雖然映射可以將任何類型的值作為鍵,但WeakMap
只接受對象並在垃圾收集對象鍵時刪除WeakMap
中的值。此外, WeakMap
無法迭代,這意味著您必須訪問對對象鍵的引用才能訪問值。這使得它對於創建私有變量非常有用,因為變量實際上是不可見的。
class CarModule { constructor() { this.data = new WeakMap(); this.data.set(this, { milesDriven: 0, speed: 0 }); this.getMilesDriven = () => this.data.get(this).milesDriven; } accelerate(amount) { // 在這個版本中,我們改為創建一個WeakMap 並// 使用"this" 關鍵字作為鍵,這不太可能// 被意外地用作WeakMap 的鍵。 const data = this.data.get(this); const speed = data.speed amount; const milesDriven = data.milesDriven data.speed; this.data.set({ speed, milesDriven }); } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.data); //=> WeakMap { [items unknown] } -- 此數據無法從外部輕鬆訪問!
此解決方案擅長防止意外使用數據,但它並非真正私有,因為它仍然可以通過用CarModule
替換this
從外部訪問。此外,它增加了相當多的複雜性,因此並不是最優雅的解決方案。
使用符號防止衝突
如果目的是防止名稱衝突,則可以使用Symbol
提供一個有用的解決方案。這些本質上是可以作為唯一值行為的實例,它們永遠不會等於任何其他值,除了它自己的唯一實例。以下是它在實際應用中的示例:
class CarModule { constructor() { this.speedKey = Symbol("speedKey"); this.milesDrivenKey = Symbol("milesDrivenKey"); this[this.speedKey] = 0; this[this.milesDrivenKey] = 0; } accelerate(amount) { // 此數據幾乎不可能被意外訪問。它決不是私有的, // 但它遠離任何將要實現此模塊的人。 this[this.speedKey] = amount; this[this.milesDrivenKey] = this[this.speedKey]; } getMilesDriven() { return this[this.milesDrivenKey]; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); // => undefined -- 我們需要訪問內部鍵才能訪問變量。 與下劃線解決方案一樣,此方法或多或少依賴於命名約定來防止混淆。
TC39 私有類字段提案
最近,提出了一項新的提案,該提案將向類引入私有變量。它很簡單:在變量名稱前加上#,它就變成私有的。無需額外的結構更改。
class CarModule { #speed = 0 #milesDriven = 0 accelerate(amount) { // 此數據幾乎不可能被意外訪問。 this.#speed = amount; this.#milesDriven = speed; } getMilesDriven() { return this.#milesDriven; } } const testCarModule = new CarModule(); testCarModule.accelerate(5); testCarModule.accelerate(4); console.log(testCarModule.getMilesDriven()); console.log(testCarModule.speed); //=> undefined -- 我們需要訪問內部鍵才能訪問變量。
私有類特性已經成為現實,並且已經擁有相當好的瀏覽器支持。
結論
這就是在JavaScript 中實現私有變量的各種方法的總結。沒有一種“正確”的方法。這些方法適用於不同的需求、現有代碼庫和其他約束。雖然每種方法都有其優點和缺點,但最終,只要它們有效地解決了您的問題,所有方法都是同樣有效的。
感謝您的閱讀!我希望這能為您提供一些關於如何應用作用域和變量私有性來改進您的JavaScript 代碼的見解。這是一種強大的技術,可以支持許多不同的方法,並使您的代碼更易於使用且無錯誤。自己嘗試一些新示例,獲得更好的感覺。
以上是在JavaScript中實現私人變量的詳細內容。更多資訊請關注PHP中文網其他相關文章!

對於Astro,我們可以在構建過程中生成大部分網站,但是有一小部分服務器端代碼可以使用Fuse.js之類的搜索功能來處理搜索功能。在此演示中,我們將使用保險絲搜索一組個人“書籤”


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

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

WebStorm Mac版
好用的JavaScript開發工具

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

SublimeText3 Linux新版
SublimeText3 Linux最新版