深刻理解Javascript中構造函數和原型對象的區別(轉存)

 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()(設置),進行原型對象的讀寫操做。

 

 

  來作一個小小的總結,上面對一些屬性和方法的介紹均可以歸結爲一句話:

  構造函數自己的屬性沒法被對象實例共享,而原型對象上的屬性和方法能夠被所用對象實例所共享。

相關文章
相關標籤/搜索