JavaScript(5)— 面向對象 + 原型

面向對象 + 原型

面向對象這個概念並不陌生,如 C++、Java 都是面向對象語言。面向對象而言都會現有一個類的概念 ,先有類再有對象。類是實例的類型模板。

比如人類 是一個類 張三 李四 就是一個個對象,他們都是人類創建出的對象 所以都有人類的共同特性,比如 人類都會吃飯 人類都會走路 所以張三李四也會吃飯和走路。

JavaScript 沒有類的概念,是基於原型的面向對象方式。它們的區別在於:

在基於類的面向對象方式中,對象(object)依靠類(class)來產生。  在基於原型的面向對象方式中,對象(object)則是依靠構造函數(constructor)和原型(prototype)構造出來的。

面向對象語言的第一個特性毫無疑問是封裝,在 JS 中,封裝的過程就是把一些 屬性方法 放到對象中「包裹」起來。

一、創建對象三種方式

1、原始方式創建對象

1) 字面量的方式

示例

        var per = {              name: "張三",              age: 20,              sex: "男",              say: function () {                  console.log("說話");              }          };

2) Object實例添加屬性方法

示例

           var per2=new Object();             per2.name="李四";             per2.age=30;             per2.sex="男";             per2.say=function () {               console.log("說話");             };

優點:程式碼簡單。

缺點: 創建多個對象會產生大量的程式碼,編寫麻煩,且並沒有實例與原型的概念。

解決辦法:工廠模式。

2、工廠模式

概念 工廠模式是非常常見的一種設計模式,它抽象了創建具體對象的過程。JS 中創建一個函數,把創建新對象、添加對象屬性、返回對象的過程放到這個函數中,

用戶只需調用函數來生成對象而無需關注對象創建細節。

示例

    function createObject(name,age) {        this.name=name;        this.age=age;        this.say=function () {          console.log("說話");        };      }      var per1=createObject("張三",20);      var per2=createObject("李四",30);

優點:工廠模式解決了對象字面量創建對象程式碼重複問題,創建相似對象可以使用同一API。

缺點:因為是調用函創建對象,無法識別對象的類型。

解決辦法:構造函數

3、構造函數

JS 中構造函數與其他函數的唯一區別,就在於調用它的方式不同。任何函數,只要通過new 操作符來調用,那它就可以作為構造函數。

示例

   //自定義構造函數----->實例化對象      function Person(name,age,sex) {        this.name=name;        this.age=age;        this.sex=sex;        this.say=function () {          console.log("說話");        };      }      //構造函數---->創建對象      var per1=new Person("張三",20,"女");      var per2=new Person("李四",30,"女");

通過構造函數new一個實例經歷了四步:

1. 創建一個新對象;  2. 將構造函數內的 this 綁定到新對象上;  3. 為新對象添加屬性和方法;  4. 返回新對象(JS 引擎會默認添加 return this;)。

而通過構造函數創建的對象都有一個constructor屬性,它是一個指向構造函數本身的指針,因此就可以檢測對象的類型。

alert(per1.constructor === Person) //true  alert(per1 instanceof Person) // true

但是仍然存在問題:

alert(per1.say == per2.say) //false

同一個構造函數中定義了say(),而不同對象的同名函數卻是不相等的,意味著這兩個同名函數的記憶體空間不一致,也就是構造函數中的方法要在每個實例上重新創建一次。

這顯然增加不必要記憶體空間。

優點:解決了類似對象創建問題,且可以檢測對象類型。

缺點:構造函數方法要在每個實例上新建一次。

解決辦法:原型模式。

二、原型模式

1、概念

在JS中,創建對象的方式有工廠模式和構造函數模式等; 而構造函數模式最大的問題在於:構造函數中的每個方法都需要在實例對象中重新創建一遍,不能復用

所以為了解決這一個問題,就需要使用原型模式來創建對象。原型模式是把所有實例共享的方法和屬性放在一個叫做 prototype(原型)的屬性中 ,在創建一個函數

時都會有個prototype屬性, 這個屬性是一個指針,指向一個對象,是通過調用構造函數而創建的那個對象實例的原型對象。

如果你學習過java,我們可以簡單理解原型就好比我們的靜態方法,任何對象都可以共享這個靜態方法。

作用 共享數據,節省記憶體空間。

2、舉例

使用原型,就意味著我們可以把希望實例共享的屬性和方法放到原型對象中去,而不是放在構造函數中,這樣每一次通過構造函數new一個實例,原型對象中定義

的方法都不會重新創建一次。

示例

//原型的作用之一:共享數據,節省記憶體空間  function Person() {  }  //通過構造函數的原型添加屬性和方法  Person.prototype.name = "張三";  Person.prototype.age = "20";  Person.prototype.say = function() {    alert('通過原型創建吃飯方法');  };    var person1 = new Person();  var person2 = new Person();  alert(person1.name); //"張三"  alert(person2.name); //"張三"  alert(person1.say == person2.say); //true 通過原型創建的方法就為true

優點:與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約記憶體。

缺點:使用空構造函數,實例 person1 和 person2 的 name都一樣了,我們顯然不希望所有實例屬性方法都一樣,它們還是要有自己獨有的屬性方法。
並且如果原型中對象中有引用類型值,實例中獲得的都是該值的引用,意味著一個實例修改了這個值,其他實例中的值都會相應改變。

解決辦法:構造函數+原型模式組合使用。

三、構造函數+原型模式

最後一種方式就是組合使用構造函數和原型模式,構造函數用於定義實例屬性,而共享屬性和方法定義在原型對象中。這樣每個實例都有自己獨有的屬性,

同時又有對共享方法的引用,節省記憶體。

    //原型的作用之一:共享數據,節省記憶體空間      //構造函數      function Person(age,sex) {        this.age=age;        this.sex=sex;      }      //通過構造函數的原型添加一個方法      Person.prototype.eat=function () {        console.log("通過原型創建吃飯方法");      };        var per1=new Person(20,"男");      var per2=new Person(20,"女");      alert(per1.eat == per2.eat); //通過原型創建的方法就為true

這種構造函數與原型模式混成的模式,是目前在 JS 中使用最為廣泛的一種創建對象的方法。

參考

1、JS面向對象編程之封裝 基本上參考這篇寫的,因為我認為它寫的非常通俗易懂,不需要我再去整理了。非常感謝

2、js面向對象編程

3、JavaScript面向對象

別人罵我胖,我會生氣,因為我心裡承認了我胖。別人說我矮,我就會覺得好笑,因為我心裡知道我不可能矮。這就是我們為什麼會對別人的攻擊生氣。  攻我盾者,乃我內心之矛(2)。