原型與原型鏈一直是JavaScript的重難點,掌握這部份內容將會使咱們的工做更加的高效,並且這也是面試官必問的內容。javascript
首先,咱們要明確的是,在 ES6 以前,咱們建立一個實例並非經過類(class
),而是直接使用構造函數來實現的。java
經過 new 函數名
來實例化對象的函數叫構造函數。任何的函數均可以做爲構造函數存在。構造函數首字母通常大寫。面試
那麼,咱們使用構造函數來建立一個對象。瀏覽器
function Person(name , age , sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
var person = new Person("Tony" , 18 , "男");
console.log(person.name); // Tony
複製代碼
上面這段代碼就是建立一個了Person
的構造函數,在Person
構造函數中,爲每個對象都添加了三個屬性(name,age,sex
),也就是說構造函數每執行一次就會建立一個新的Person
對象。markdown
構造函數簡單複習一下,下面步入正題。函數
prototype
在JS中,每當定義一個函數時候,都會默認自帶一個prototype
屬性,這個屬性指向的是該構造函數建立的實例的原型,而且這個屬性是一個對象數據類型的值。ui
原型:每個JS對象(null
除外)在建立的時候就會與之關聯另外一個對象,這個對象就是咱們所說的原型,每個對象都會從原型"繼承"屬性。this
舉個原型的Demo:spa
function Person() { }
Person.prototype.name = 'Tony'; // 注意:prototype是函數纔會有的屬性
var person1 = new Person();
var person2 = new Person();
console.log(person1.name); // Tony
console.log(person2.name); // Tony
複製代碼
構造函數和實例原型之間的關係: 在這裏 Person.prototype
表示實例原型。原型對象就至關於一個公共的區域,全部同一個類的實例均可以訪問到這個原型對象,咱們能夠將對象中共有的內容,統一設置到原型對象中。prototype
上面,咱們說明了實例和實例原型,那麼咱們該怎麼表示這二者之間的關係呢?這時候咱們就要說到下面兩個屬性了
__proto__
這是每個JS 對象(除了 null
)都具備的一個屬性,叫__proto__
,這個屬性會指向該對象的原型。
person.__proto__ === Person.prototype // true
複製代碼
__proto__
絕大部分瀏覽器都支持這個非標準的方法訪問原型,然而它並不存在於 Person.prototype
中,實際上,它是來自於 Object.prototype
,與其說是一個屬性,不如說是一個 getter/setter
,當使用 obj.__proto__
時,能夠理解成返回了 Object.getPrototypeOf(obj)
。
constructor
每一個原型都有一個 constructor
屬性指向關聯的構造函數。
Person === Person.prototype.constructor // true
複製代碼
當獲取 person.constructor
時,其實 person
中並無 constructor
屬性,當不能讀取到constructor
屬性時,會從 person
的原型也就是 Person.prototype
中讀取,正好原型中有該屬性,因此:
person.constructor === Person.prototype.constructor
複製代碼
實例原型與構造函數的關係圖: 原型鏈:在JS中,萬物皆對象,對象和對象之間也是有關係得,並非孤立存在的。對象之間的繼承關係,在JS 中是經過prototype
對象指向父類對象,直到指向Object
對象爲止,這樣就造成了一個原型指向的鏈條,專業術語稱之爲原型鏈。
當咱們讀取實例的一個屬性或方法時,它會先在對象自身中尋找,若是有則直接使用,若是沒有則會去原型對象中尋找,若是找到則直接使用。若是沒有則去原型的原型中尋找,一直找到最頂層Object
爲止,Object
對象的原型沒有原型(Object是JS中全部對象數據類型的基類(最頂層的類)在Object.prototype
上沒有__proto__
這個屬性),若是在Object
原型中依然沒有找到,則返回undefined
。
function Person() {}
Person.prototype.name = 'Tony';
var person = new Person();
// 當咱們給實例對象person添加了name屬性,打印 person.name 時,結果爲name的值Ken。
person.name = 'Ken';
console.log(person.name) // Ken
// 當咱們刪除了person的name屬性時,再次讀取person.name,從person 對象中找不到name屬性就會從person的原型也就是person.__proto__和Person.prototype中查找name屬性,由於以前咱們給他添加了,因此找到了 name屬性爲 Tony。
delete person.name;
console.log(person.name) // Tony
複製代碼
咱們能夠使用對象的hasOwnProperty()
來檢查對象自身中是否含有該屬性,若是自身屬性存在,則返回 true
,不然爲false
;使用in
檢查對象中是否含有某個屬性時,若是對象中沒有可是原型中有,則會返回true
,若是都沒有則返回false
。
function Person() {
this.name = 'Tony'
}
Person.prototype.age = 18;
var person = new Person();
console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age')); // false
console.log('name' in person); // true
console.log('age' in person); // true
console.log('a' in person); // false
複製代碼
最後,相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線:
用心讀完上面的總結,應該會對 JS 的原型與原型鏈有了一個深層次的認識吧,其實這部分仍是須要細心琢磨的,畢竟是比較底層的原理。