JS物件導向編程詳解,js物件導向編程

來源:互聯網
上載者:User

JS物件導向編程詳解,js物件導向編程

序言
  在JavaScript的大世界裡討論物件導向,都要提到兩點:1.JavaScript是一門基於原型的物件導向語言 2.類比類語言的物件導向方式。對於為什麼要類比類語言的物件導向,我個人認為:某些情況下,原型模式能夠提供一定的便利,但在複雜的應用中,基於原型的物件導向系統在抽象性與繼承性方面差強人意。由於JavaScript是唯一一個被各大瀏覽器支援的指令碼語言,所以各路高手不得不使用各種方法來提高語言的便利性,最佳化的結果就是其編寫的代碼越來越像類語言中的物件導向方式,從而也掩蓋了JavaScript原型系統的本質。  

基於原型的物件導向語言
  原型模式如類模式一樣,都是是一種編程泛型,即編程的方法論。另外最近大紅大紫的函數編程也是一種編程泛型。JavaScript之父Brendan Eich在設計JavaScript時,從一開始就沒打算為其加入類的概念,而是借鑒了另外兩門基於原型的的語言:Self和Smalltalk。

  既然同為物件導向語言,那就得有建立對象的方法。在類語言中,對象基於模板來建立,首先定義一個類作為對現實世界的抽象,然後由類來執行個體化對象;而在原型語言中,對象以複製另一個對象的方式建立,被複製的母體稱為原型對象。

  複製的關鍵在於語言本身是否為我們提供了原生的複製方法。在ECMAScript5中,Object.create可以用來複製對象。

var person = {  name: "tree",  age: 25,  say: function(){    console.log("I'm tree.")  }};var cloneTree = Object.create(person);console.log(cloneTree);


  原型模式的目的並不在於得到一個一模一樣的對象,而提供了一種便捷的方式去建立對象(出自《JavaScript設計模式與開發實踐》)。但是由於語言設計的問題,JavaScript的原型存在著諸多矛盾,它的某些複雜的文法看起來就那些基於類的語言,這些文法問題掩蓋了它的原型機制(出自《JavaScript語言精粹》)。如:

function Person(name, age){  this.name = name;  this.age = age;      }var p = new Person('tree', 25)

  實際上,當一個函數對象唄建立時,Function構造器產生的函數對象會運行類似這樣的一些代碼:

this.prototype = {constructor: this}

  新的函數對象被賦予一個prototype屬性,它的值是一個包含constructor屬性且屬性值為該新函數的對象。當對一個函數使用new運算子時,函數的prototype的屬性的值被作為原型對象來複製出新對象。如果new運算子是一個方法,它的執行過程如下:

Function.prorotype.new = function() {  //以prototype屬性值作為原型對象來複製出一個新對象  var that = Object.create(this.prorotype);    //改變函數中this關鍵指向這個新複製的對象  var other = this.apply(that, arguments);    //如果傳回值不是一個對象,則返回這個新複製對象  return (other && typeof other === 'object') ? other : that;}

  從上面可以看出,雖然使用new運算子調用函數看起來像是使用模板執行個體化的方式來建立對象,但本質還是以原型對象來複製出新對象。

   由於新複製的對象能否訪問到原型對象的一切方法和屬性,加上new運算子的特性,這便成了利用原型類比類式語言的基石。 

利用原型類比類式語言
抽象

  用原型模式來類比類,首先是抽象方式。根據JavaScript語言的特點,通常一個類(實際上是偽類)通常是將欄位放置於建構函式(實際上是new 運算子調用的函數,JavaScript本身並沒有建構函式的概念)中,而將方法放置於函數的prototype屬性裡。

function Person(name, age) {  this.name = name;  this.age = age;};Person.prototype.say = function(){  console.log("Hello, I'm " + this.name);};

繼承

  繼承是OO語言中的一個最為人津津樂道的概念。許多OO語言都支援兩種繼承方式:介面繼承和實現繼承。介面繼承之繼承方法簽名,而實現繼承則繼承實際的方法。但是ECMAScript中無法實現介面繼承,只支援實現繼承,而且其實現繼承主要是依靠原型鏈來實現的。(出自《JavaScript進階程式設計》 6.3節——繼承)在高三中作者探索了各種關於繼承的類比,如:組合繼承、原型繼承、寄生繼承、寄生組合繼承,最終寄生組合式成為所有類比類式繼承的基礎。

function Person(name, age) {  this.name = name;  this.age = age;};Person.prototype.say = function(){  console.log("Hello, I'm " + this.name);};function Employee(name, age, major) {  Person.apply(this, arguments);  this.major = major;};Employee.prototype = Object.create(Person.prototype);Employee.prorotype.constructor = Employee;Employee.prorotype.sayMajor = function(){  console.log(this.major);}

  高三中只給出了單繼承的解決方案,關於多繼承的類比我們還得自己想辦法。由於多繼承有其本身的困難:物件導向語言如果支援了多繼承的話,都會遇到著名的菱形問題(Diamond Problem)。假設存在一個如左圖所示的繼承關係,O中有一個方法foo,被A類和B類覆寫,但是沒有被C類覆寫。那麼C在調用foo方法的時候,究竟是調用A中的foo,還是調用B中的foo?

  所以大多數語言並不支援多繼承,如Java支援單繼承+介面的形式。JavaScript並不支援介面,要在一個不支援介面的語言上去類比介面怎麼辦?答案是著名的鴨式辨型。放到實際代碼中就是混入(mixin)。原理很簡單:

 function mixin(t, s) {    for (var p in s) {      t[p] = s[p];    }  } 

  值得一提的是dojo利用MRO(方法解析順序(Method Resolution Order),即尋找被調用的方法所在類時的搜尋順序)方式解決了多繼承的問題。  

  到此,我們已經清楚了類比類語言的基本原理。作為一個愛折騰的程式員,我希望擁有自己的方式來簡化類的建立:

  • 提供一種便利的方式去建立類,而不暴露函數的prototype屬性
  • 在子類中覆蓋父類方法時,能夠像Java一樣提供super函數,來直接存取父類同名方法
  • 以更方便的方式添加靜態變數和方法而不去關心prototype
  • 像C#那樣支援Attribute   

最終,在借鑒各位大牛的知識總結,我編寫了自己的類建立工具O.js:

(function(global) {  var define = global.define;  if (define && define.amd) {    define([], function(){      return O;    });  } else {    global.O = O;  }  function O(){};  O.derive = function(sub) {    debugger;    var parent = this;    sub = sub ? sub : {};    var o = create(parent);    var ctor = sub.constructor || function(){};//如何調用父類的建構函式?    var statics = sub.statics || {};    var ms = sub.mixins || [];    var attrs = sub.attributes || {};    delete sub.constructor;    delete sub.mixins;    delete sub.statics;    delete sub.attributes;    //處理繼承關係    ctor.prototype = o;    ctor.prototype.constructor = ctor;    ctor.superClass = parent;    //利用DefineProperties方法處理Attributes    //for (var p in attrs) {      Object.defineProperties(ctor.prototype, attrs);    //}    //靜態屬性    mixin(ctor, statics);    //混入其他屬性和方法,注意這裡的屬性是所有執行個體對象都能夠訪問並且修改的    mixin(ctor.prototype, sub);    //以mixin的方式類比多繼承    for (var i = 0, len = ms.length; i < len; i++) {      mixin(ctor.prototype, ms[i] || {});    }    ctor.derive = parent.derive;    //_super函數    ctor.prototype._super = function(f) {      debugger;      return parent.prototype[f].apply(this, Array.prototype.slice.call(arguments, 1));    }    return ctor;  }  function create(clazz) {    var F = function(){};    F.prototype = clazz.prototype;    //F.prototype.constructor = F; //不需要    return new F();  };  function mixin(t, s) {    for (var p in s) {      t[p] = s[p];    }  }})(window);

類建立方式如下:

var Person = O.derive({  constructor: function(name) {//建構函式    this.setInfo(name);  },  statics: {//靜態變數    declaredClass: "Person"  },  attributes: {//類比C#中的屬性    Name: {      set: function(n) {        this.name = n;        console.log(this.name);      },      get: function() {        return this.name + "Attribute";      }    }  },  share: "asdsaf",//變數位於原型對象上,對所有對象共用  setInfo: function(name) {//方法    this.name = name;  }});var p = new Person('lzz');console.log(p.Name);//lzzAttributeconsole.log(Person);

繼承:

var Employee = Person.derive({//子類有父類派生  constructor: function(name, age) {    this.setInfo(name, age);  },  statics: {    declaredClass: "Employee"  },  setInfo: function(name, age) {    this._super('setInfo', name);//調用父類同名方法    this.age = age;  }});var e = new Employee('lll', 25);console.log(e.Name);//lllAttributeconsole.log(Employee);

以上就是本文的全部內容,希望對大家的學習有所協助。

您可能感興趣的文章:
  • JS物件導向編程之對象流量分析
  • 物件導向的Javascript之二(介面實現介紹)
  • Javascript 物件導向(一)(共有方法,私人方法,特權方法)
  • JavaScript物件導向之Prototypes和繼承
  • javascript物件導向入門基礎詳細介紹
  • javascript物件導向封裝類Class封裝類庫剖析
  • js一般方法改寫成物件導向方法的無限級摺疊菜單範例程式碼
  • JavaScript物件導向編程入門教程
  • JS物件導向基礎講解(原廠模式、建構函式模式、原型模式、混合模式、動態原型模式)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.