js深入理解構造函數和原型對象


1.在典型的oop的語言中,如java,都存在類的概念,類就是對象的模板,對象就是類的實例。但在js中不存在類的概念,js不是基於類,而是通過構造函數(constructor)和原型鏈(prototype chains)實現的。但在ES6中引入了類(class)這個概念,作為對象的模板,新的class寫法知識讓原型對象的寫法更加清晰,這里不重點談這個

2.首先我們來詳細了解下什么是構造器

  構造函數的特點:

    a:構造函數的首字母必須大寫,用來區分於普通函數

    b:內部使用的this對象,來指向即將要生成的實例對象

    c:使用New來生成實例對象

  eg1:

 1 function Person(name,age){
 2      this.name = name;    
 3      this.age = age;   
 4      this.sayHello = function(){   
 5          console.log(this.name +"say hello");
 6     }
 7 }
 8 
 9 var boy = new Person("bella",23);    
10 boy.sayHello(); // bella say hello

  構造函數的缺點:

    所有的實例對象都可以繼承構造器函數中的屬性和方法。但是,同一個對象實例之間,無法共享屬性

    解決思路:

      a:所有實例都會通過原型鏈引用到prototype

      b:prototype相當於特定類型所有實例都可以訪問到的一個公共容器

      c:那么我們就將重復的東西放到公共容易就好了

    eg2:

 1 function Person(name,age){
 2     this.name = name;
 3     this.age = age;
 4     this.sayHello = function(){
 5         console.log(this.name + "say hello");
 6     }
 7 }
 8 var girl = new Person("bella",23);
 9 var boy = new Person("alex",23);
10 console.log(girl.name);  //bella
11 console.log(boy.name);   //alex
12 console.log(girl.sayHello === boy.sayHello);  //false

      

 

  一個構造函數Person生成了兩個對象實例girl和boy,並且有兩個屬性和一個方法。但是sayHello方法是不一樣的。如上圖(圖畫得很丑)。也就是說當New一個實例對象的時候,都會去創建一個sayHello方法,這就浪費了內存資源,因為sayHello方法使一樣的行為的,完全可以被兩個實例對象共享。

  所以,缺點就是:同一個構造函數的對象實例之間無法共享屬性和方法。

  為了解決構造函數的這個缺點,js提供了prototype屬相來解決該問題。

  prototype屬性的作用

  js中每個數據類型都是對象,除了null 和 undefined(這個可以參考另一篇將null 和 undefined的博客),而每個對象都是繼承自一個原型對象,只有null除外,它沒有自己的原型對象,最終的Object的原型為null

  eg3:

 1 function Person(name,age){
 2     this.name = name;
 3     this.age = age;
 4 }
 5 Person.propotype.sayHello = function(){
 6     console.log(this.name + "say hello");
 7 }
 8 var girl = new Person("bella",23);
 9 var boy = new Person("alex",23);
10 console.log(girl.name);  //bella
11 console.log(boy.name);   //alex
12 console.log(girl.sayHello === boy.sayHello);  //true
View Code

                   

      由上圖可以看出,prototype是構造函數的屬性,而consructor則是構造函數的prototype屬性所指向的那個對象,也就是說constuctor是原型對象的屬性。

  constructor屬性是定義在原型對象上面,意味着也可以被實例對象繼承

  eg4:

  

 1 function Person(name,age){
 2     this.name = name;
 3     this.age = age;
 4 }
 5 Person.propotype.sayHello = function(){
 6     console.log(this.name + "say hello");
 7 }
 8 var girl = new Person("bella",23);
 9 console.log(girl.construcotr); //Person()
10 console.log(girl.construcotr == Person.propotype.construcotr); //true
View Code

  constructor屬性的作用

    a:分辨原型對象到底是哪個構造函數  

1 function Person(){};
2 var person1 = new Person();
3 console.log(person1.construcotr === Person); //true
View Code

    b:從實例新建另一個實例

1 function Person(){};
2 var person1 = new Person();
3 var person2 = new person1.construcotr();
4 console.log(person2 instanceof Person); //true
View Code

    c:由於constructor屬性是一種原型對象和構造函數的關系,所以在修改原型對象的時候,一定 要注意construtor的指向問題,避免instanceof失真,關於這一點,會在繼承中講到。

 

3.了解了構造器,我們來看下原型prototype

  JS中萬物都是對象,但是對象也分為:普通對象和函數對象,也就是Object 和 Function.

  那么怎么區分普通對象和函數對象呢? ---凡是通過New Function()創建的對象都是函數對象,其他的都是普通對象.

  需要注意的是:普通對象沒有propotype(prototype即是屬性也是對象),但是有__proto__屬性。

  js創建對象的時候都有一個__propo__內置屬性,用於指向創建它的函數對象的原型對象prototype。

  我們還是來根據eg3的代碼來分析原型鏈

  console.log(girl.__proto__ === Person.protype);//true

  console.log(Persion.propotype.__proto__ === Object.propotype);//true

  console.log(Object.porpotype.__proto__); //null

  通過__proto__串起來直到Object.propotype.__proto__為null的鏈叫做原型鏈(矩形表示函數對象,橢圓形表示普通對象)

  

  也許看到這個圖會有幾個疑問

    a:為什么Object.__proto__指向Function.prototype?

      Object是函數對象,是通過new Function()創建的,所以...

    b:Function.__proto__ === Function.prototype //true

      Function也是對象函數,通過new Function()創建,所以...

 

 

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM