前端的面試中常常會遇到這個問題,本身也是一直似懂非懂,趁這個機會整理一下
1994年,網景公司(Netscape)發佈了Navigator瀏覽器0.9版,可是剛開始的Js沒有繼承機制,更別提像同時期興盛的C++和Java這樣擁有面向對象的概念。在實際的開發過程當中,工程師們發現沒有繼承機制很難解決一些問題,必須有一種機制能將全部的對象關聯起來。javascript
Brendan Eich鑑於以上狀況,但不想把Js設計得過爲複雜,因而引入了new關鍵詞 和 constructor構造函數來簡化對象的設計,引入了prototype函數對象來包含全部實例對象的構造函數的屬性和方法,引入了proto和原型鏈的概念解決繼承的問題。html
var o1 = {}; var o2 =new Object(); var o3 = new f1(); function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)');
凡是直接或者間接經過
new Function()
建立的對象都是函數對象,其餘的都是普通對象。
在上面的程序中,o一、o二、o3都是普通對象,f一、f二、f3都是函數對象。前端
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name) } } var person1 = new Person('Zaxlct', 28, 'Software Engineer'); var person2 = new Person('Mick', 23, 'Doctor');
person1
和person2
都是 構造函數 Person 的實例。
實例的構造函數屬性(constructor)指向構造函數。
Person.prototype = { name: 'Zaxlct', age: 28, job: 'Software Engineer', sayName: function() { alert(this.name); } }
每一個函數對象都有一個 prototype
屬性,這個屬性就是函數的原型對象。java
每一個對象都有 proto 屬性,但只有函數對象纔有 prototype 屬性
在默認狀況下,全部的原型對象都會自動得到一個 constructor(構造函數)屬性,這個屬性(是一個指針)指向 prototype 屬性所在的函數(Person)面試
Person.prototype.constructor == Person
而在Person這個對象進行實例化的時候,其實是建立了一個它的實例對象並賦值給它的 prototype,因此得出如下結論:瀏覽器
原型對象(Person.prototype)是 構造函數(Person)的一個實例。
JS 在建立對象(不管是普通對象仍是函數對象)的時候,都有一個叫作__proto__ 的內置屬性,用於指向建立它的構造函數的原型對象,也就是prototype。網絡
Person.prototype.constructor == Person; person1.__proto__ == Person.prototype; person1.constructor == Person;
看下面一段代碼app
Person.prototype.__proto__ === Object.prototype;
Person.prototype
是一個普通對象,咱們無需關注它有哪些屬性,只要記住它是一個普通對象。
由於一個 普通對象的構造函數 === Object
因此 Person.prototype.__proto__ === Object.prototype
ide
原型鏈的造成是真正是靠__proto__ 而非prototype。
person1.__proto__ === Person.prototype; Person.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null; //Object.prototype.__proto__ === null,保證原型鏈可以正常結束。 Person.__proto__ === Function.prototype; Object.__proto__ === Function.prototype; Function.prototype.__proto__ === Object.prototype;
前面三項已經造成了一個原型鏈,那麼後面代碼中Person和Object的__proto__都是Function.prototype
呢?函數
全部函數對象的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 Object.__proto__ === Function.prototype // true Object.constructor == Function // true 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
因此咱們再舉一個原型鏈的例子
let num = new Number(); num.__proto__ === Number.prototype; Number.prototype.__proto__ === Function.prototype; Funtion.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null;
也可看下圖的實現,更直觀。
ok, 因此明白爲何Number、String、Array這樣的對象實例能繼承到Object的屬性以及原型鏈是怎麼一回事了吧。
參考文章
最詳盡的 JS 原型與原型鏈終極詳解,沒有「多是」。
三張圖搞懂JavaScript的原型對象與原型鏈 - 水乙 - 博客園
Javascript繼承機制的設計思想 - 阮一峯的網絡日誌