搜尋
首頁web前端css教學在JavaScript中實現私人變量

Implementing Private Variables In JavaScript

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 正式發布,隨之而來的是兩個新的變量關鍵字: letconst 。兩者都是塊作用域的,這意味著用這些關鍵字創建的變量可以被同一對括號內的任何內容訪問。與var相同,但letconst變量無法在循環、函數、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());

首先要注意的是, milesDrivenspeed變量位於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中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
模擬鼠標運動模擬鼠標運動Apr 22, 2025 am 11:45 AM

如果您曾經在現場演講或課程中必須顯示一個互動動畫,那麼您可能知道它並不總是那麼容易與您的幻燈片進行互動

通過Astro Action和Fuse.js為搜索提供動力通過Astro Action和Fuse.js為搜索提供動力Apr 22, 2025 am 11:41 AM

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

未定義:第三個布爾值未定義:第三個布爾值Apr 22, 2025 am 11:38 AM

我想在我的一個項目中實現一條通知消息,類似於您在保存文檔時在Google文檔中看到的信息。換句話說,一個

捍衛三元聲明捍衛三元聲明Apr 22, 2025 am 11:25 AM

幾個月前,我正在使用黑客新聞(就像一個人一樣),並且遇到了一篇(現已刪除的)文章,內容涉及不使用if語句。如果您是這個想法的新手(就像我

使用網絡語音API進行多語言翻譯使用網絡語音API進行多語言翻譯Apr 22, 2025 am 11:23 AM

自科幻小說以來,我們就幻想著與我們交談的機器。今天這很普遍。即便如此,製造的技術

JetPack Gutenberg塊JetPack Gutenberg塊Apr 22, 2025 am 11:20 AM

我記得當古騰堡被釋放到核心時,因為那天我在WordCamp我們。現在已經過去了幾個月,所以我想我們越來越多的人

在VUE中創建可重複使用的分頁組件在VUE中創建可重複使用的分頁組件Apr 22, 2025 am 11:17 AM

大多數Web應用程序背後的想法是從數據庫中獲取數據,並以最佳方式將其呈現給用戶。當我們處理數據時

使用'盒子陰影”和剪輯路徑一起使用'盒子陰影”和剪輯路徑一起Apr 22, 2025 am 11:13 AM

讓我們在一個情況下做一些似乎有意義的事情的情況下逐步進行一些逐步,但是您仍然可以用CSS欺騙來完成它。在這個

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

mPDF

mPDF

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

SublimeText3 英文版

SublimeText3 英文版

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

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版