詳解 JavaScript 構造函數和 "new" 操做符

構造器和操做符 "new"

常規的 {...} 語法容許建立一個對象。可是咱們常常須要建立許多相似的對象,例如多個用戶或菜單項等。javascript

這可使用構造函數和 "new" 操做符來實現。html

構造函數

構造函數在技術上是常規函數。不過有兩個約定:java

  1. 它們的命名以大寫字母開頭。
  2. 它們只能由 "new" 操做符來執行。

例如:react

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");

alert(user.name); // Jack
alert(user.isAdmin); // false

當一個函數被使用 new 操做符執行時,它按照如下步驟:算法

  1. 一個新的空對象被建立並分配給 this
  2. 函數體執行。一般它會修改 this,爲其添加新的屬性。
  3. 返回 this 的值。

換句話說,new User(...) 作的就是相似的事情:微信

function User(name) {
  // this = {};(隱式建立)

  // 添加屬性到 this
  this.name = name;
  this.isAdmin = false;

  // return this;(隱式返回)
}

因此 new User("Jack") 的結果是相同的對象:函數

let user = {
  name: "Jack",
  isAdmin: false
};

如今,若是咱們想建立其餘用戶,咱們能夠調用 new User("Ann")new User("Alice") 等。比每次都使用字面量建立要短得多,並且更易於閱讀。學習

這是構造器的主要目的 —— 實現可重用的對象建立代碼。測試

讓咱們再強調一遍 —— 從技術上講,任何函數均可以用做構造器。即:任何函數均可以經過 new 來運行,它會執行上面的算法。「首字母大寫」是一個共同的約定,以明確表示一個函數將被使用 new 來運行。this

new function() { ... }

若是咱們有許多行用於建立單個複雜對象的代碼,咱們能夠將它們封裝在構造函數中,像這樣:

let user = new function() {
  this.name = "John";
  this.isAdmin = false;

  // ……用於用戶建立的其餘代碼
  // 也許是複雜的邏輯和語句
  // 局部變量等
};

構造器不能被再次調用,由於它不保存在任何地方,只是被建立和調用。所以,這個技巧旨在封裝構建單個對象的代碼,而無需未來重用。

構造器模式測試:new.target

進階內容:

本節涉及的語法內容不多使用,除非你想了解全部內容,不然你能夠直接跳過該語法。

在一個函數內部,咱們可使用 new.target 屬性來檢查它是否被使用 new 進行調用了。

對於常規調用,它爲空,對於使用 new 的調用,則等於該函數:

function User() {
  alert(new.target);
}

// 不帶 "new":
User(); // undefined

// 帶 "new":
new User(); // function User { ... }

它能夠被用在函數內部,來判斷該函數是被經過 new 調用的「構造器模式」,仍是沒被經過 new 調用的「常規模式」。

咱們也可讓 new 調用和常規調用作相同的工做,像這樣:

function User(name) {
  if (!new.target) { // 若是你沒有經過 new 運行我
    return new User(name); // ……我會給你添加 new
  }

  this.name = name;
}

let john = User("John"); // 將調用重定向到新用戶
alert(john.name); // John

這種方法有時被用在庫中以使語法更加靈活。這樣人們在調用函數時,不管是否使用了 new,程序都能工做。

不過,處處都使用它並非一件好事,由於省略了 new 使得很難觀察到代碼中正在發生什麼。而經過 new 咱們均可以知道這建立了一個新對象。

構造器的 return

一般,構造器沒有 return 語句。它們的任務是將全部必要的東西寫入 this,並自動轉換爲結果。

可是,若是這有一個 return 語句,那麼規則就簡單了:

  • 若是 return 返回的是一個對象,則返回這個對象,而不是 this
  • 若是 return 返回的是一個原始類型,則忽略。

換句話說,帶有對象的 return 返回該對象,在全部其餘狀況下返回 this

例如,這裏 return 經過返回一個對象覆蓋 this

function BigUser() {

  this.name = "John";

  return { name: "Godzilla" };  // <-- 返回這個對象
}

alert( new BigUser().name );  // Godzilla,獲得了那個對象

這裏有一個 return 爲空的例子(或者咱們能夠在它以後放置一個原始類型,沒有什麼影響):

function SmallUser() {

  this.name = "John";

  return; // <-- 返回 this
}

alert( new SmallUser().name );  // John

一般構造器沒有 return 語句。這裏咱們主要爲了完整性而說起返回對象的特殊行爲。

省略括號

順便說一下,若是沒有參數,咱們能夠省略 new 後的括號:

let user = new User; // <-- 沒有參數
// 等同於
let user = new User();

這裏省略括號不被認爲是一種「好風格」,可是規範容許使用該語法。

構造器中的方法

使用構造函數來建立對象會帶來很大的靈活性。構造函數可能有一些參數,這些參數定義瞭如何構造對象以及要放入什麼。

固然,咱們不只能夠將屬性添加到 this 中,還能夠添加方法。

例如,下面的 new User(name) 用給定的 name 和方法 sayHi 建立了一個對象:

function User(name) {
  this.name = name;

  this.sayHi = function() {
    alert( "My name is: " + this.name );
  };
}

let john = new User("John");

john.sayHi(); // My name is: John

/*
john = {
   name: "John",
   sayHi: function() { ... }
}
*/

是用於建立複雜對象的一個更高級的語法,咱們稍後會講到。

總結

  • 構造函數,或簡稱構造器,就是常規函數,但你們對於構造器有個共同的約定,就是其命名首字母要大寫。
  • 構造函數只能使用 new 來調用。這樣的調用意味着在開始時建立了空的 this,並在最後返回填充了值的 this

咱們可使用構造函數來建立多個相似的對象。

JavaScript 爲許多內置的對象提供了構造函數:好比日期 Date、集合 Set 以及其餘咱們計劃學習的內容。

對象,咱們還會回來噠!

在本章中,咱們只介紹了關於對象和構造器的基礎知識。它們對於咱們在下一章中,學習更多關於數據類型和函數的相關知識很是重要。

在咱們學習了那些以後,咱們將回到對象,在 info:prototypesinfo:classes 章節中深刻介紹它們。

做業題

先本身作題目再看答案。

1. 兩個函數 — 一個對象

重要程度:⭐️⭐️

是否能夠建立像 new A()==new B() 這樣的函數 AB

function A() { ... }
function B() { ... }

let a = new A;
let b = new B;

alert( a == b ); // true

若是能夠,請提供一個它們的代碼示例。

2. 建立 new Calculator

重要程度:⭐️⭐️⭐️⭐️⭐️

建立一個構造函數 Calculator,它建立的對象中有三個方法:

  • read() 使用 prompt 請求兩個值並把它們記錄在對象的屬性中。
  • sum() 返回這些屬性的總和。
  • mul() 返回這些屬性的乘積。

例如:

let calculator = new Calculator();
calculator.read();

alert( "Sum=" + calculator.sum() );
alert( "Mul=" + calculator.mul() );

3. 建立 new Accumulator

重要程度:⭐️⭐️⭐️⭐️⭐️

建立一個構造函數 Accumulator(startingValue)

它建立的對象應該:

  • 將「當前 value」存儲在屬性 value 中。起始值被設置到構造器 startingValue 的參數。
  • read() 方法應該使用 prompt 來讀取一個新的數字,並將其添加到 value 中。

換句話說,value 屬性是全部用戶輸入值與初始值 startingValue 的總和。

下面是示例代碼:

let accumulator = new Accumulator(1); // 初始值 1

accumulator.read(); // 添加用戶輸入的 value
accumulator.read(); // 添加用戶輸入的 value

alert(accumulator.value); // 顯示這些值的總和

答案:

在微信公衆號「技術漫談」後臺回覆 1-4-5 獲取做業答案。


現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 並列的 JavaScript 學習教程

在線免費閱讀:https://zh.javascript.info


掃描下方二維碼,關注微信公衆號「技術漫談」,訂閱更多精彩內容。

相關文章
相關標籤/搜索