本文共 1475 字,讀完只需 6 分鐘
在 JavaScript 中,是一種面向對象的程序設計語言,可是 JS 自己是沒有 「類」 的概念,JS 是靠原型和原型鏈實現對象屬性的繼承。 javascript
在理解原型前,須要先知道對象的構造函數是什麼,構造函數都有什麼特色?java
1. 構造函數瀏覽器
// 構造函數 Person() function Person(name, gender) { this.name = name; this.gender = gender; } var person = new Person("周杰倫", "男"); // 最後建立出來的對象實例 person person { name: "周杰倫", gender: "男" }
以上代碼,普通函數 Person()
,加上 new
關鍵字後,就構造了一個對象 person
微信
因此構造函數的定義就是普通函數加上 new
關鍵字,並總會返回一個對象。app
2. 函數對象
同時,JS 中的對象分爲通常對象和函數對象。那什麼是通常對象,什麼又是函數對象呢? 函數
JavaScript 的類型分爲基本數據類型和引用數據類型,基本數據類型目前有 6 種(null, undefined, string, number, boolean, Symbol)。 其他的數據類型都統稱爲 object 數據類型,其中,包括 Array, Date, Function等,因此函數能夠稱爲函數對象。this
let foo = function(){ } foo.name = "bar"; foo.age = 24; console.log(foo instanceof Function) //true console.log(foo.age) // 24
以上代碼就說明了函數實際上是一個對象,也能夠具備屬性。spa
JavaScript 中的對象,有一個特殊的 [[prototype]]
屬性, 其實就是對於其餘對象的引用(委託)。當咱們在獲取一個對象的屬性
時,若是這個對象上沒有這個屬性,那麼 JS 會沿着對象的 [[prototype]]
鏈 一層一層地去找,最後若是沒找到就返回 undefined
;prototype
這條一層一層的查找屬性的方式,就叫作原型鏈。設計
var o1 = { age: 6 }
那麼,爲何一個對象要引用,或者說要委託另一個對象來尋找屬性呢?
本文開篇的第一句話,就指出來的,JavaScript 中,和通常的 OOP 語言不一樣,它沒有 '類'的概念,也就沒有 '模板' 來建立對象,而是經過字面量或者構造函數的方式直接建立對象。那麼也就不存在所謂的類的複製繼承。
那什麼又是原型呢?
既然咱們沒有類,就用其餘的方式實現類的行爲吧,看下面這句話↓↓。
1. 每一個函數都有一個原型屬性 prototype 對象
function Person() { } Person.prototype.name = 'JayChou'; // person1 和 person2 都是空對象 var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // JayChou console.log(person2.name) // JayChou
經過構造函數創造的對象,對象在尋找 name
屬性時,找到了 構造函數的 prototype
對象上。
這個構造函數的 prototype
對象,就是 原型
用示意圖來表示:
查找對象實例屬性時,會沿着原型鏈向上找,在現代瀏覽器中,標準讓每一個對象都有一個 __proto__
屬性,指向原型對象。那麼,咱們能夠知道對象實例和函數原型對象之間的關係。
2. 每一個原型對象都有一個 constructor 屬性指向關聯的構造函數
爲了驗證這一說話,舉個例子。
function Person() {} Person === Person.prototype.constructor; // true
那麼對象實例是構造函數構造而來,那麼對象實例是否是也應該有一個 constructor
呢?
function Person() {} const person = new Person(); person.constructor === Person // true
但事實上,對象實例自己並無 constructor
屬性,對象實例的 constructor 屬性來自於引用了原型對象的 constructor
屬性
person.constructor === Person.prototype.constructor // true
3. 原型鏈頂層:Object.prototype.__proto__ == null
既然 JS 經過原型鏈查找屬性,那麼鏈的頂層是什麼呢,答案就是 Object 對象,Object 對象其實也有 __proto__
屬性,比較特殊的是 Object.prototype.__proto__
指向 null
, 也就是空。
Object.prototype.__proto__ === null
咱們回過頭來看函數對象:
全部函數對象的proto都指向Function.prototype,它是一個空函數(Empty function)
Number.__proto__ === Function.prototype // true Number.constructor == Function //true Boolean.__proto__ === Function.prototype // true Boolean.constructor == Function //true String.__proto__ === Function.prototype // true String.constructor == Function //true // 全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身 Object.__proto__ === Function.prototype // true Object.constructor == Function // true // 全部的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身 Function.__proto__ === Function.prototype // true Function.constructor == Function //true Array.__proto__ === Function.prototype // true Array.constructor == Function //true RegExp.__proto__ === Function.prototype // true RegExp.constructor == Function //true Error.__proto__ === Function.prototype // true Error.constructor == Function //true Date.__proto__ === Function.prototype // true Date.constructor == Function //true
全部的構造器都來自於 Function.prototype,甚至包括根構造器Object及Function自身。全部構造器都繼承了·Function.prototype·的屬性及方法。如length、call、apply、bind
以圖會友,這就是網上常常看到的 JS 原型和原型鏈關係圖:
對於以上看似很複雜的關係圖,只須要理解 5 點:
Person.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__
是 null以上就是 JavaScript 中原型和原型鏈的知識。因爲 JS 沒有'類', 因此採用了原型的方式實現繼承,正確的說法是引用或者委託,由於對象之間的關係不是複製,而是委託。在查找屬性的時候,引用(委託)原型對象的屬性,也就是咱們常說的原型繼承。
歡迎關注我的微信訂閱號,專一分享原創文章