JavaScript 起步:“new” 運算符

編程語言 JavaScript 虐殺原形 Chrome 忠實玩家 2017-06-08

四項原則

理解 new 運算符最簡單的辦法就是搞明白它是幹什麼的。當你使用 new 的時候,會發生4件事情:

  1. 它創建一個新的空對象。

  2. 將 this 綁定到新創建的對象上。

  3. 在新創建的對象上添加一個叫“proto” 的屬性,這個屬性指向構造函數的原型(prototype)對象。

  4. 在函數結束前添加return this,這樣剛創建的對象可以從這這個函數返回出去。

如果不能完全理解這些規則也沒關係。我們從簡單的開始。創建一個叫 Student 的構造函數。這個函數有兩個參數,name 和 age。然後會將這兩個參數的值設置給 this 的屬性。

function Student(name, age) {

不過現在用 new 調用我們的構造函數。我會傳入兩個參數:'John' 和 26。

var first = **new** Student('John', 26);
  1. 創建了一個新對象 —— first 對象

  2. this 綁定到 first 對象。所有 this 引用指向 first。

  3. 添加proto。first.proto 現在指向 Studnet.prototype。

  4. 所有事情完成後,我們將新的 first 對象返回出來賦值給新的 first 變量。

現在我們來運行一個簡單的 console.log 語句測試代碼是否可運行:

console.log(first.name);

非常棒。現在我們深入 new 關鍵字的 proto 部分

原型

每個 JavaScript 對象都有原型。JavaScript 的所有對象都從它們的原型繼承方法和屬性。

來看一個示例。打開你的 Chrome 開發者工具 控制檯(Windows:Ctrl+Shift+J)(Mac: Cmd+Option+J),然後輸入本文前面定義的 Students 函數。

function Student(name, age) {

為了證明每個對象都有原型,我們現在輸入:

Student.prototype;

酷,返回了一個對象。現在嘗試創建一個新的學生對象:

var second = new Student('Jeff', 50);

我們已經使用了 Studnet 構造函數來創建第二個名為 Jeff 的學生。而且,因為我們使用 new 運算符,proto 屬性應該已經添加到了我人的 second 對象中。它會指向父構造器。來看一下它們是否相等:

second.__proto__ === Student.prototype;

最後,我們的 Student.prototype.constructor 會指向 Student 構造函數:

Student.prototype.constructor;

這很快就得複雜起來。讓我們看看下面的簡圖,看能否讓你直觀地瞭解到這個過程:

JavaScript 起步:“new” 運算符

很漂亮,不是嗎?

正如你在上面看到的,我們的 Student 構造函數 (其它構造函數也是一樣)有一個叫 .prototype 的屬性。這個原型上有一個對象叫 .constrcutor,往回指向構造函數。這是一個漂亮的小回環。然後,在我們使用 new 運算符創建新對象的時候,每個對象都有 .proto 屬性將新對象與 Student.prototype 連接起來。

為什麼這很重要?

因為繼承,它非常重要。原型對象由對應構造函數創建的所有對象共享。也就是說我們可以在原型中添加函數和屬性,而所有對象都可以使用。

在我們上面的示例中,我們只創建了兩個 Student 對象,但是如果不是 2 個學生,而是 20,000 個呢?我們在原型上定義函數而不是在每個學生對象上定義這種做法一下子就節省了大量的處理能力。

讓我們來看一個回到理想之家的例子。在控制檯輸入下面的代碼:

Student.prototype.sayInfo = function(){

再次說明,這裡所做的事情是為 Student 原型增加一個函數——現在任何我們創建的學生對象可以訪問這個新的 .sayInfo 函數!來測試一下:

second.sayInfo();

添加一個新的學生然後再次測試:

var third = new Student('Tracy', 15);

有效果!有效果是因為……JavaScript 對象會首先查找是否有我們調用的屬性。如果沒有,它會向上查找,來到原型這裡,然後說“嗨,你有這個屬性嗎?”這種模式一直持續到我們找到屬性,或者達到原型鏈的終點。

因為相同的原因你可以通過繼承使用像 .toString() 這樣已經定義的方法!正因如此,既然你從來沒有寫過 toString() 方法,也可以好好的使用它。因為這個方法和其它內建的 JS 方法一樣,定義在 Object 的原型上。我們創建的每個對象最終都會找到 Object 原型上去。當然,我們可以重寫這些方法:

var name = {

我們的對象首先檢查自己是否有需要的方法,然後才會到原型中去找。既然我們已經定義了這個方法,那直接運行就好,不需要使用繼承的方法了。但這並不是個好主意。最好保留全局方法,為你自己的方法換個別的名稱。

小結

作為新入行的開發者,可能很難理解這些概念,不過一旦理解了,就能寫出更好更有效的代碼。通過原型我們可以快速而有效地在成百上千個對象中共享相同的代碼。和我其它所有文章一樣,這篇文章是讓你瞭解一些基礎知識,然後你可以自己繼續深入學習。

相關推薦

推薦中...