Object是構造函數,而Object.prototype是構造函數的原型對象。構造函數自身的屬性和方法沒法被共享,而原型對象的屬性和方法能夠被全部實例對象所共享。javascript
首先,咱們知道,構造函數是生成對象的模板,一個構造函數能夠生成多個對象,每一個對象都有相同的結構。構造函數的缺點就是,每當你實例化兩個對象時,須要調用兩次構造函數的某一個方法,這帶來的壞處就是佔用內存,並且不必。java
其次,爲了解決構造函數的屬性和方法沒法被對象實例所共享的問題,咱們能夠把須要共享的屬性和方法放在原型(prototype)對象上。原型對象上的全部屬性和方法,都會被對象實例所共享。對於構造函數來講,prototype是做爲構造函數的屬性;對於對象實例來講,prototype是對象實例的原型對象。因此prototype便是屬性,又是對象。數組
而後,除了undefined和null以外,每個數據類型均可以當作一個對象,每個對象都有它的原型。全部一切對象的原型頂端,都是Object.prototype,即Object構造函數的prototype屬性指向的那個對象。固然,Object.prototype對象也有本身的原型對象,那就是沒有任何屬性和方法的null對象,而null對象沒有本身的原型。瀏覽器
原型鏈的特色有:框架
a:讀取對象的某個屬性時,JavaScript引擎先尋找對象自己的屬性,若是找不到,就到它的原型去找,若是仍是找不到,就到原型的原型去找。若是直到最頂層的Object.prototype
仍是找不到,則返回undefined
。函數
b:若是對象自身和它的原型,都定義了一個同名屬性,那麼優先讀取對象自身的屬性,這叫作「覆蓋」(overiding)。性能
c:一級級向上在原型鏈尋找某個屬性,對性能是有影響的。所尋找的屬性在越上層的原型對象,對性能的影響越大。若是尋找某個不存在的屬性,將會遍歷整個原型鏈。ui
再次,constructor屬性是原型對象上的一個屬性,能夠被全部實例對象所共享。要注意的是,prototype是構造函數的屬性,而constructor則是構造函數的prototype屬性所指向的那個對象,也就是原型對象的屬性。因爲constructor屬性是一種原型對象和構造函數的關係,因此在修改原型對象的時候,必定要注意constructor的指向問題。spa
最後,instanceof運算符返回一個布爾值,用於判斷對象是否爲某個構造函數的實例。prototype
在接下來的分享中,會談談Object的部分方法和Object.prototoype的部分方法。雖然都是概念性問題,可是若是理解了這些概念,對於MVVM框架和各類js框架的理解都有至關大的幫助。
如下的分享會分爲以下內容:
1.Object和Object.prototype的區別
2.Object.getPrototypeOf()
3.Object.setPrototypeOf()
4.Object.create()
5.Object.prototype.isPrototypeOf()
6.Object.prototype.__proto__
1.Object和Object.prototype的區別
我的認爲,要學好javascript的其中一個方法就是,必須理解每個" . "所表明的意思是什麼,是調用自身的屬性和方法呢,仍是繼承原型的對象的屬性和方法。來看看Object構造函數和構造函數的原型Object.prototype都有哪些屬性和方法。
Object是構造函數,而Object.prototype是構造函數的原型對象。構造函數自身的屬性和方法沒法被共享,而原型對象的屬性和方法能夠被全部實例對象所共享。
Object的屬性和方法:
Object.prototype的屬性和方法:
上面例子中,Object擁有本身的方法prototype,getPrototypeOf(),setPrototypeOf()等,這些方法沒法被實例所共享。而Object.prototypeOf()的hasOwnProperty,isPrototypeOf(),constructor等屬性和方法是能夠被實例對象所共享的。舉一個最簡單的例子。
1 function Keith() {} 2 var a = new Keith(); 3 console.log(a.prototype); //undefined 4 console.log(a.constructor); //Keith()
上面代碼中,構造函數Keith是沒有任何屬性和方法的。當訪問prototype屬性時返回undefined,是由於prototype屬性沒有辦法從構造函數中繼承,只能由構造函數自己訪問。而constructor返回了Keith(),由於constructor屬性自己就是Object.prototype中的屬性,能夠被全部實例對象所共享。
那麼問題來了,如何知道實例對象的原型呢?能夠經過Object.isPrototypeOf方法和繼承原型對象的isPrototypeOf方法實現。
1 console.log(Keith.prototype.isPrototypeOf(a)); //true 2 console.log(Object.getPrototypeOf(a) === Keith.prototype) //true
上面代碼中,實例對象a的原型就是Keith.prototype。這兩個屬性會稍後介紹。
2.Object.getPrototypeOf()
Object.getPrototypeOf
方法返回一個對象的原型。這是獲取原型對象的標準方法。
1 // 空對象的原型是Object.prototype 2 console.log(Object.getPrototypeOf({}) === Object.prototype) // true 3 4 // 函數的原型Function.prototype 5 function keith() {} 6 console.log(Object.getPrototypeOf(keith) === Function.prototype) //true 7 8 // 數組的原型Array.prototype 9 var arr = [1,2,3]; 10 console.log(Object.getPrototypeOf(arr) === Array.prototype) ///true
3.Object.setPrototypeOf()
Object.setPrototypeOf方法能夠爲現有對象設置原型,而後返回一個新對象。這個能夠接收兩個參數,第一個是現有對象,第二個是原型對象。
1 var keith = { 2 height: 180 3 }; 4 var rascal = Object.setPrototypeOf({}, keith); 5 console.log(rascal.height); //180 6 7 //上下兩個代碼片斷相同。 8 var keith = { 9 height: 180 10 }; 11 var rascal ={ 12 __proto__: keith 13 }; 14 console.log(rascal.height); //180
上面代碼中,rascal對象是Object.setPrototypeOf
方法返回的一個新對象。該對象自己爲空、原型爲keith對象,因此rascal對象能夠拿到keith對象的全部屬性和方法。rascal對象自己並無height屬性,可是JavaScript引擎找到它的原型對象keith,而後讀取keith的height屬性。
4.Object.create()
Object.create方法用於從原型對象生成新的對象實例,能夠代替new命令。它接受一個參數,這個參數爲所要繼承的原型對象,而後返回一個實例對象。
1 var Keith = { 2 hobby : function() { 3 return 'Watching Movies'; 4 } 5 }; 6 7 var rascal = Object.create(Keith); 8 console.log(rascal.hobby()) //'Watching Movies'
上面代碼中,Object.create方法將Keith對象做爲rascal的原型對象,此時rascal就繼承了Keith對象中的全部屬性和方法。rascal就成爲了Keith對象的實例對象。用下面這段代碼比較好理解。
1 function Keith() {}; 2 Keith.prototype.hobby = function() { 3 return 'Watching Movies'; 4 } 5 6 var rascal = Object.create(Keith); 7 console.log(rascal.hobby()) //'Watching Movies';
new操做符和Object.create方法都是返回一個對象實例,可是二者有一些區別。
1 function Keith() {} 2 var a = new Keith(); 3 var b = Object.create(Keith.prototype); 4 5 console.log(a instanceof Keith); //true 6 console.log(b instanceof Keith); //true
上面代碼中,可使用new操做符來調用構造函數,返回對象實例;而Object.create傳入的參數必須是構造函數Keith的原型。
實際上,若是有老式瀏覽器不支持Object.create方法,能夠用下面這段代碼來構造一個Object.create方法。
1 if (typeof Object.create !=='function') { 2 Object.create = function(x) { 3 function F() {}; 4 F.prototype = x; 5 return new F(); 6 }; 7 }
下面這三種方式生成的實例對象都是等價的。
1 var o1 = Object.create({}); 2 var o2 = Object.create(Object.prototype); 3 var o2 = new Object();
在使用Object.create方法時,要注意的是必須傳入原型對象,不然會報錯。
1 var o1 = Object.create(); 2 console.log(o1);//TypeError: Object.create requires more than 0 arguments
Object.create方法生成的對象實例,動態繼承了原型對象。也就是說,修改原型對象的屬性和方法會反應在對象實例上。
1 var keith = { 2 height:180 3 }; 4 5 var rascal = Object.create(keith); 6 keith.height=153; 7 console.log(rascal.height) //153
上面代碼中,修改原型對象,會影響生成的對象實例。
Object.create方法生成的對象,繼承了它的原型對象的構造函數。
1 function Keith() {}; 2 var boy = new Keith(); 3 var girl = Object.create(boy); 4 console.log(Object.getPrototypeOf(girl) === boy); //true 5 console.log(girl.constructor === Keith); //true 6 console.log(girl instanceof Keith); //true
上面代碼中,girl對象的原型是boy對象,girl對象的constructor屬性指向了原型對象boy的構造函數Keith。
5.Object.prototype.isPrototypeOf()
對象實例的isPrototypeOf方法,用於判斷一個對象對象是不是另一個對象的原型。
1 var o1 = {}; 2 var o2 = Object.create(o1); 3 var o3 = Object.create(o2); 4 5 console.log(o1.isPrototypeOf(o2)); //true 6 console.log(o2.isPrototypeOf(o3)); //true 7 console.log(o1.isPrototypeOf(o3)); //true
上面代碼中,能夠看出,只要某個對象處於原型鏈上,isPrototypeOf都返回true。
1 function Keith() {}; 2 3 console.log(Function.prototype.isPrototypeOf(Keith)); //true 4 console.log(Object.prototype.isPrototypeOf(Function)); //true 5 console.log(Object.getPrototypeOf(Object.prototype) === null); //true
上面代碼中,構造函數Keith的原型指向了Function.prototype,而構造函數Function的原型指向了Object.prototype。Object的原型指向了沒有任何屬性和方法的null對象。
6.Object.prototype.__proto__
__proto__屬性(先後兩條下劃線)能夠改寫某個對象的原型對象。這個屬於實例方法。
1 var keith = {}; 2 var rascal = {}; 3 rascal.__proto__ = keith; 4 console.log(keith.isPrototypeOf(rascal)); //true
上面代碼中,經過rascal對象的__proto__屬性,將rascal的原型指向了keith對象。
__proto__
屬性只有瀏覽器才須要部署,其餘環境能夠沒有這個屬性,並且先後的兩根下劃線,表示它本質是一個內部屬性,不該該對使用者暴露。所以,應該儘可能少用這個屬性,而是用Object.getPrototypeof()
(讀取)和Object.setPrototypeOf()
(設置),進行原型對象的讀寫操做。
來作一個小小的總結,上面對一些屬性和方法的介紹均可以歸結爲一句話:
構造函數自己的屬性沒法被對象實例共享,而原型對象上的屬性和方法能夠被所用對象實例所共享。