前置任務
在說原型對象是什么之前,我們先討論一下對象是什么東西
在說對象是什么之前,我們又得討論一下引用類型
引用類型


首先,js 中變量的值分兩種類型
- 引用類型
- 值類型
關於這兩種類型,我們需要從內存的角度來看
var num = 9527 //值類型
var str = "一段字符串" // 值類型
var obj = { // 引用類型
attr_1:"qwer",
attr_2:"df"
}
上面這些數據,在內存中可能是這樣的

可以看到
- 值類型的
num和str兩個變量,變量名直接對應具體值 - 引用類型的
obj這個變量對應的是一段地址,而這個地址的位置存的才是真正的obj的具體值(對象)
至於為什么要這么存,這跟內存的管理有關就不展開說,簡單的
你媽媽給你生了五個可愛的妹妹
for(var i=0;i<5;i++){
var 妹妹i號=new 妹妹()
}
每個妹妹都是new出來的一個對象,她們都有一些屬性,比如
妹妹1號:{
age:3
name:妹妹1號,
parent:{
媽媽:你的媽媽,
爸爸:你的爸爸
}
}
每個妹妹的
age和name屬性都是不同的,而parent屬性都是相同的,這時候如果每個妹妹都存一份parent就太浪費內存了,所以我們可以存個地址.內存中這個地址的位置存真正的parent信息,這樣就可以很好的利用起寶貴的內存空間啦
ps: 我們建立一個概念,一個對象是一個獨立的'塊',而不是妹妹i號.parent這樣一條屬性,妹妹i號.parent這條屬性指向一個對象,也不用糾結,先往下看
對象
前面我們說了,對象是獨立的塊內存,要想訪問或者操作對象,就得通過該對象的的地址,而變量存儲的就是這個地址
然后我們來看
var obj = {
attr_1: "qwer",
attr_2: "df"
};
var obj_2=obj
obj_2.attr_1="qwqaqaaaaaawer"
console.log(obj.attr_1) //qwqaqaaaaaawer
這樣,為什么改的是obj_2.attr_1而打印obj.attr_1的時候是qwqaqaaaaaawer應該就很清楚了
原型對象
無論什么時候,只要創建了一個新函數,就會根據一組特定的規則為該函數創建一個 prototype 屬性,這個屬性指向函數的原型對象。
注意兩點
- 函數的
prototype屬性指向函數的原型對象,而不是說prototype就是原型對象,prototype是地址,內存中這個地址的位置上的東西才是原型對象- 函數也是對象,所以函數也可以有屬性

拓展閱讀: 為什么要創建原型對象
在默認情況下,所有原型對象都會自動獲得一個 constructor(構造函數)屬性,這個屬性包含一個指向 prototype 屬性所在函數(下圖這個例子中的a)的地址。看圖理解:

到目前為止,內存中是這樣的
思考題:為什么a.prototype.constructor==a
答案:a.prototype.constructor和a指向同一塊內存
上面說,創建了自定義的構造函數之后,其原型對象默認會取得 constructor 屬性

然鵝:


這個a.prototype.toString函數根本沒有定義,上面的內存圖中也看不到它,那它是從哪哪冒出來的???
至於其原型對象的其他方法,則都是從 Object 繼承而來的。

(這個__proto__是什么,看下面的詳細講解)
當調用構造函數創建一個新實例后(是一個對象),該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。
es5 中管這個指針叫[[Prototype]]。雖然在 js 中沒有標准的方式訪問[[Prototype]],
但 Firefox、Safari 和 Chrome 在每個對象(函數的原型對象也是對象,所以也有__proto__屬性)上都支持一個屬性__proto__;
再來看一遍這段代碼:
a.prototype指向原型對象,原型對象是由構造函數Ojbect生成的Object也是一個函數,是Object.prototype指向Object的原型對象- 思考題:
a.prototype.__proto__是函數a的原型對象的一條屬性,這個屬性的屬性值是一個地址,那么內存中這個地址存的是什么?
答案: 存的是Object的原型對象
附內存圖一張,方便理解:
這個連接存在於實例的原型對象與構造函數的原型對象之間,而不是存在於實例與構造函數之間。
a的原型對象是由構造函數Object生成的,他們兩個之間存在鏈接(通過__proto__)
接着說,js中,有這樣一條規則:訪問一條屬性(假設是屬性attr)時,在當前對象(假設是obj)中找不到的,就往obj.__proto__找,即obj.__proto__.attr,再找不到,就往obj.__proto__.__proto__找,直到找到或者obj.__proto__.......為null才停止
所以 前面的a.prototype.toString實際上是a.prototype.__proto__.toString也就是Object.tostring




