前置任務 在說原型對象是什麼之前,我們先討論一下 對象 是什麼東西 在說對象是什麼之前,我們又得討論一下 引用類型 引用類型 ![](https://ae01.alicdn.com/kf/U511530ba0ea143d69c43f6a3ac1feba0a.jpg ) 首先,js 中變數的值分兩種類 ...
前置任務
在說原型對象是什麼之前,我們先討論一下對象是什麼東西
在說對象是什麼之前,我們又得討論一下引用類型
引用類型
首先,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出來的一個對象,她們都有一些屬性,比如
js 妹妹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