通常談到js中的繼承的時候,必定會遇到原型,原型鏈的問題,原型裏面又有prototype,__proto__,constructor屬性,講到這兒,不少同窗是否是都一頭霧水,傻傻分不清楚,由於工做中用到的地方是少之又少,再加上es6又出了extends語法糖,更加不用理會之,可是對於理解繼承,原型和原型鏈是很重要的,理解函數和對象,理解prototype和__proto__,construct之間的關係尤其重要,不過本文對繼承不予以深究,另起一篇文章寫之,今天咱們只討論js中的原型和原型鏈。javascript
首先,容在下提出一個問題。
到底prototype和__proto__是否是指同一個東西呢?
答案天然非也。java
還有一個問題,就是ie8,9下是沒有__proto__的概念的,如何解決這個問題?
這個問題在這篇文章結束以前會說明。es6
如今咱們先來分析js中的對象。
js中對象是很重要的,所謂萬物皆對象,可是js中對象分爲兩種,普通對象和函數對象函數
先來看幾個例子this
function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 = new Object(); console.log(typeof Object); //function console.log(typeof Function); //function console.log(typeof o1); //object console.log(typeof o2); //object console.log(typeof o3); //object console.log(typeof f1); //function console.log(typeof f2); //function console.log(typeof f3); //function
在上面的例子中,o1 o2 o3 爲普通對象,f1 f2 f3 爲函數對象。
那麼怎麼區分普通對象和函數對象呢?
其實很簡單,凡是經過new Function()建立的對象都是函數對象,其餘的都是普通對象。f1,f2,歸根結底都是經過 new Function()的方式進行建立的。Function Object 也都是經過 New Function()建立的。.net
接下來先說一下原型對象
在js中,每當定義一個對象的時候,對象中都會包含一些預約義的屬性。其中函數對象的一個屬性就是原型對象prototype。
普通對象沒有prototype,只有__proto__屬性,看下面的例子prototype
function f1(){}; console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object} console.log(typeof f1.prototype) //Object console.log(typeof Function.prototype) // Function,這個特殊,由於Function是經過new Function建立的 console.log(typeof Object.prototype) // Object console.log(typeof Function.prototype.prototype) //undefined
從console.log(f1.prototype) //{constructor:ƒ f1(),__proto__:Object}能夠看出f1.prototype就是f1的一個實例對象,就是在建立f1的時候,建立了一個它的實例對象,並把它賦給了prototype原型對象。代碼以下設計
const temp = new f1(); f1.prototype = temp;
那麼看到這兒,你們確定會說,爲何要有原型對象?這個原型對象有什麼用?
剛開始我就提到了,繼承裏會用到。看下下面的代碼:code
function Cat(name){ this.name = name; } Cat.prototype.getName = function(){ alert(this.name); } const qqq = new Cat('qqq'); qqq.getName();//qqq
從上面的代碼中能夠看出,經過給Cat的原型對象添加屬性方法,那麼Cat的實例都會擁有這個方法並能夠調用之。有同窗可能會有疑問,爲何在原型對象上添加了屬性方法,它的實例就也能夠擁有這個方法呢?這就牽扯到接下來講到的原型鏈了。對象
首先,js的對象(普通對象和函數對象)都會有__proto__屬性,指向建立它的構造函數的原型對象,好比上面的例子
qqq.__proto__ === Cat.prototype;//true Cat.prototype.__proto__ === Object.prototype;//true Object.prototype.__proto__//null
這就造成了原型鏈,會一直查找原型對象的__proto__屬性,直到爲null。
有幾個比較特殊的例子,來看一下
1.Object.__proto__ === Function.prototype // true
Object是函數對象,是經過new Function()建立,因此Object.__proto__指向Function.prototype。
2.Function.__proto__ === Function.prototype // true
Function 也是對象函數,也是經過new Function()建立,因此Function.__proto__指向Function.prototype。
3.Function.prototype.__proto__ === Object.prototype //true
Function.prototype是個函數對象,理論上他的__proto__應該指向 Function.prototype,就是他本身,本身指向本身,沒有意義。
JS一直強調萬物皆對象,函數對象也是對象,給他認個祖宗,指向Object.prototype。Object.prototype.__proto__ === null,保證原型鏈可以正常結束。
constructor是這麼定義的。
在 Javascript 語言中,constructor 屬性是專門爲 function 而設計的,它存在於每個 function 的prototype 屬性中。這個 constructor 保存了指向 function 的一個引用。
Cat.prototype.constructor === Cat //true Function.prototype.constructor === Function //true Object.prototype.constructor === Object //true
這裏也有要注意的
1.注意Object.constructor===Function;//true 自己Object就是Function函數構造出來的
2.如何查找一個對象的constructor,就是在該對象的原型鏈上尋找碰到的第一個constructor屬性所指向的對象
1.原型和原型鏈是實現繼承的一種方式
2.原型鏈真正的繼承是靠__proto__,而不是prototype,且看如下這個例子
var animal = function(name){ this.name = name; } var cat = function(){}; animal.say = 'lalala'; cat.prototype = animal; var ca = new cat(); console.log(cat.say);//undefined console.log(ca.say);//lalala
從輸出結果能夠看出,雖然cat的prototype指向了animal,可是讀取say屬性的時候並不會根據prototype找,ca自己雖然也沒有say屬性,可是看下面這段代碼
ca.__proto__ = cat.prototype cat.prototype = animal
因此ca.say輸出lalala
3.以前遺留的問題,關於兼容ie的__proto__
ie9有Object.getPrototypeof()方法
function a(){console.log("aaa")}; const b = new a(); Object.getPrototypeof(b) === b.__proto__//true
ie8不支持Object.getPrototypeof方法,能夠結合constructor和prototype
function a(){console.log("aaa")}; const b = new a(); b.constructor.prototype === b.__proto__//true
最後再思考下new()過程都作了些什麼?(好比new A())
好了,今天就先寫這麼多,明天結合今天的原型和原型鏈總結下繼承以及每一個繼承的優缺點~~~