天天看點

js建立對象| 25

JavaScript對每個建立的對象都會設定一個原型,指向它的原型對象。

當我們用​

​obj.xxx​

​通路一個對象的屬性時,JavaScript引擎先在目前對象上查找該屬性,如果沒有找到,就到其原型對象上找,如果還沒有找到,就一直上溯到​

​Object.prototype​

​對象,最後,如果還沒有找到,就隻能傳回​

​undefined​

​。

例如,建立一個​

​Array​

​對象:

var arr = [1, 2, 3];      

其原型鍊是:

arr ----> Array.prototype ----> Object.prototype ----> null      

​Array.prototype​

​定義了​

​indexOf()​

​、​

​shift()​

​等方法,是以你可以在所有的​

​Array​

​對象上直接調用這些方法。

當我們建立一個函數時:

function foo()
    return 0;
}      

函數也是一個對象,它的原型鍊是:

foo ----> Function.prototype ----> Object.prototype ----> null      

由于​

​Function.prototype​

​定義了​

​apply()​

​等方法,是以,所有函數都可以調用​

​apply()​

​方法。

很容易想到,如果原型鍊很長,那麼通路一個對象的屬性就會因為花更多的時間查找而變得更慢,是以要注意不要把原型鍊搞得太長。

構造函數

除了直接用​

​{ ... }​

​建立一個對象外,JavaScript還可以用一種構造函數的方法來建立對象。它的用法是,先定義一個構造函數:

function Student(name)
    this.name = name;
    this.hello = function ()
        alert('Hello, ' + this.name + '!');
    }
}      

你會問,咦,這不是一個普通函數嗎?

這确實是一個普通函數,但是在JavaScript中,可以用關鍵字​

​new​

​來調用這個函數,并傳回一個對象:

var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!      

注意,如果不寫​

​new​

​,這就是一個普通函數,它傳回​

​undefined​

​。但是,如果寫了​

​new​

​,它就變成了一個構造函數,它綁定的​

​this​

​指向新建立的對象,并預設傳回​

​this​

​,也就是說,不需要在最後寫​

​return this;​

​。

新建立的​

​xiaoming​

​的原型鍊是:

xiaoming ----> Student.prototype ----> Object.prototype ----> null      

也就是說,​

​xiaoming​

​的原型指向函數​

​Student​

​的原型。如果你又建立了​

​xiaohong​

​、​

​xiaojun​

​,那麼這些對象的原型與​

​xiaoming​

​是一樣的:

xiaoming ↘
xiaohong -→ Student.prototype ----> Object.prototype ----> null      

用​

​new Student()​

​建立的對象還從原型上獲得了一個​

​constructor​

​屬性,它指向函數​

​Student​

​本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true      

看暈了吧?用一張圖來表示這些亂七八糟的關系就是:

js建立對象| 25

紅色箭頭是原型鍊。注意,​

​Student.prototype​

​指向的對象就是​

​xiaoming​

​、​

​xiaohong​

​的原型對象,這個原型對象自己還有個屬性​

​constructor​

​,指向​

​Student​

​函數本身。

另外,函數​

​Student​

​恰好有個屬性​

​prototype​

​指向​

​xiaoming​

​、​

​xiaohong​

​的原型對象,但是​

​xiaoming​

​、​

​xiaohong​

​這些對象可沒有​

​prototype​

​這個屬性,不過可以用​

​__proto__​

​這個非标準用法來檢視。

現在我們就認為​

​xiaoming​

​、​

​xiaohong​

​這些對象“繼承”自​

​Student​

​。

不過還有一個小問題,注意觀察:

xiaoming.name; // '小明'
xiaohong.name; // '小紅'
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false      

​xiaoming​

​和​

​xiaohong​

​各自的​

​name​

​不同,這是對的,否則我們無法區分誰是誰了。

​xiaoming​

​和​

​xiaohong​

​各自的​

​hello​

​是一個函數,但它們是兩個不同的函數,雖然函數名稱和代碼都是相同的!

如果我們通過​

​new Student()​

​建立了很多對象,這些對象的​

​hello​

​函數實際上隻需要共享同一個函數就可以了,這樣可以節省很多記憶體。

function Student(name)
    this.name = name;
}

Student.prototype.hello = function ()
    alert('Hello, ' + this.name + '!');
};      

繼續閱讀