javascriptRemke之原型的重要性

前言:JavaScript的原型對象一直是新人學習js的一大重大阻礙,但是原型的知識往往又是面試中常常會被深挖的一個點,為什麼會這樣呢?本文帶你揭秘JavaScript原型的重要性,了解重要性之後再進行學習效果會更佳。

1、從工廠模式說起

  眾所周知,工廠模式在軟件工程領域的運用極其廣泛,以下舉例工廠模式創建一個對象。

 1 function createCar(name,num){
 2     //創建一個空對象
 3     let obj = {};
 4     //為空對象添加屬性
 5     obj.name = name;
 6     obj.num = num;
 7     return obj
 8 }
 9 const myCar = createCar('benz',123456)
10 console.log (myCar); //{name: 'benz', num: 123456}

  上述案例中,createCar()方法接收兩個參數,一個name,一個num,通過參數傳入,然後返回新對象,最後調用函數即可快速創建多個屬性不同的對象。

  這樣的好處是能夠快速創建大量屬性不同的對象,但是新創建的對象沒有明確的標識,即無法得知新創建的對象是什麼類型,使用instanceOf無法準確查明。通過構造函數可以解決標識問題並也能批量創建對象。為此,我們引出構造函數的概念。

 

2、構造函數模式

  ES中的構造函數是用於創建特定類型對象的,如Object和Array這樣的原生構造函數,運行時就可以直接使用。但是我們也可以使用構造函數來定義屬性和方法。

  以下為使用構造函數模式創建的對象的例子:

1 const Person = function(name,age){
2     this.name=name;
3     this.age=age;
4 }
5 const p1 = new Person('Billy',21);
6 console.log(p1);//{name:Billy,age:21}

  上述例子同樣也可以通過多次new來創建大量屬性不同的對象 ,所創建的對象與工廠模式創建的對象類似,但是仍有以下區別:

  • 沒有顯示創建對象
  • 屬性和方法直接賦值給了this
  • 沒有return
  • 解決了標識問題

  這樣看似創建的對象天衣無縫,又能批量創建、又有標識,但是如果使用構造函數創建對象需要在對象上捆綁方法時,就會存在極大缺陷。代碼如下:

1 const Person = function(name,age){
2     this.name=name;
3     this.age=age;
4     this.sayName(){
5         console.log(this.name)
6     }
7 }
8 const p1 = new Person('Billy',21);

  構造函數的主要問題就存在於此,構造函數定義的方法會在每個實例上都創建一遍,即每次定義函數時都會初始化一個對象。即可以看似為:

1 const Person = function(name,age){
2     this.name=name;
3     this.age=age;
4     this.sayName = new Function(){
5         "console.log(this.name)"
6     };
7 }
8 const p1 = new Person('Billy',21);

  因此,以這種方法創建函數會帶來不同的作用域鏈和標識符解析,不同實例上函數雖然同名但是卻不相等。因為都是一樣的效能,所以沒有必要定義兩個不同的函數。

  要解決這個問題,可以把函數定義在全局,即從外部插入函數。

1 function sayName(){
2     console.log(this.name);
3 }
4 const Person = function(name,age){
5     this.name=name;
6     this.age=age;
7     this.sayName = sayName;
8 }
9 const p1 = new Person('Billy',21);

  這樣便解決了相同邏輯的函數重複定義的問題,但是這樣會污染全局命名空間,若這個對象需要多個方法,則需要在全局作用域定義大量函數,這樣會嚴重污染全局作用域。因此使用原型模式便成為了唯一方法。

 

3、原型模式

  使用原型對象的好處是在上面定義的屬性和方法可以被對象實例共享。代碼如下:

1 function Person3(){
2     Person3.prototype.name='Billy';
3     Person3.prototype.age=21;
4 }
5 const p7 = new Person3;
6 const p8 = new Person3;
7 console.log(p7.name);  //Billy
8 console.log(p7.name);  //Billy

  綁定在構造函數上的屬性實例都可以取到。這樣便實現了屬性共享,在構造函數中綁定函數也可以共享,這便解決了之前的所有問題。由此可見原型的重要性 :

  • 新創建的對象都有明顯標識。
  • 可共享方法,且不會重複定義。
  • 不會污染全局命名空間。