CoffeeScript類

2018-08-25 13:54 更新
在js中是否要模擬傳統(tǒng)編程語(yǔ)言的類,是個(gè)一直以來(lái)都有爭(zhēng)議的話題,不同的項(xiàng)目,不同的團(tuán)隊(duì),在類的使用上會(huì)有不同的看法,不過(guò),一旦決定要使用類,那么至少需要一套良好的實(shí)現(xiàn),CoffeeScript在語(yǔ)言內(nèi)部實(shí)現(xiàn)了類的模擬,我們來(lái)看一看一個(gè)完整的例子


class Gadget
  @CITY = "beijing"


  @create: (name, price) ->
    new Gadget(name, price)


  _price = 0


  constructor: (@name, price) ->
    _price = price


  sell: =>
    "Buy #{@name} with #{_price} in #{Gadget.CITY}"


iphone = new Gadget("iphone", 4999)
console.log iphone.name #=> "iphone"
console.log iphone.sell() #=> "Buy iphone with 4999 in beijing"


ipad = Gadget.create("ipad", 3999)
console.log ipad.sell() #=> "Buy ipad with 3999 in beijing"
這個(gè)Gadget類具有通常語(yǔ)言中類的功能:


constructor是構(gòu)造函數(shù),必須用這個(gè)名稱,類似ruby中的initialize
name是實(shí)例變量,可以通過(guò)iphone.name獲取
構(gòu)造函數(shù)中如果給實(shí)例變量賦值,直接將@name寫(xiě)在參數(shù)中即可,等價(jià)于在函數(shù)體中的@name = name
_price是私有變量,需要賦初始值
sell是實(shí)例方法
create是類方法,注意這里使用了@create,這和ruby有些像,在定義時(shí)的this指的是這個(gè)類本身
CITY是類變量
要注意的是,對(duì)于實(shí)例方法,要用=>來(lái)綁定this,這樣可以作為閉包傳遞,比如


iphone = new Gadget("iphone", 4999)
$("#sell").click(iphone.sell())
如果不用=>,閉包被調(diào)用時(shí)就會(huì)丟失實(shí)例對(duì)象的值(iphone)


對(duì)于熟悉基于類的面向?qū)ο缶幊痰娜?,CoffeeScript的類是一目了然的,下面來(lái)看看對(duì)應(yīng)的js代碼


  var Gadget,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };


  Gadget = (function() {
    var _price;


    Gadget.name = 'Gadget';


    Gadget.CITY = "beijing";


    Gadget.create = function(name, price) {
      return new Gadget(name, price);
    };


    _price = 0;


    function Gadget(name, price) {
      this.sell = __bind(this.sell, this);
      this.name = name;
      _price = price;
    }


    Gadget.prototype.sell = function() {
      return "Buy " + this.name + " with " + _price + " in " + Gadget.CITY;
    };


    return Gadget;


  })();


以上的代碼有很多值得注意的地方


整體上來(lái)說(shuō),CoffeeScript的類模擬使用的是一個(gè)*構(gòu)造函數(shù)閉包*,這是最常用的模擬類的模式,好處是可以完整地封裝內(nèi)部變量,且可以使用new來(lái)生成實(shí)例對(duì)象
_price就是被封裝在閉包內(nèi)部的私有變量
sell這樣的實(shí)例方法是原型方法,并且在初始化時(shí)使用自定義的bind函數(shù)綁定實(shí)例(用=>定義的情況)
create和CITY這樣的類成員使用構(gòu)造函數(shù)的屬性實(shí)現(xiàn),重復(fù)一下,在CoffeeScript類定義中的this指的是整個(gè)閉包Gadget

Gadget.name是額外定義的類名屬性



類的繼承

CoffeeScript中為方便地實(shí)現(xiàn)類的繼承也定義了自己的語(yǔ)法,我們把上面的類簡(jiǎn)化,來(lái)看一下如何繼承:

class Gadget
  constructor: (@name) ->
  sell: =>
    "Buy #{@name}" 


class IPhone extends Gadget
  constructor: -> super("iphone")
  nosell: =>
    "Don't #{@sell()}"


iphone = new IPhone
iphone.nosell() #=> Don't Buy iphone
使用extends關(guān)鍵字可以繼承父類中的所有實(shí)例屬性,比如sell
super方法可以調(diào)用父類的同名方法
如果不覆蓋constructor,則她被子類默認(rèn)調(diào)用
來(lái)看一下對(duì)應(yīng)的js代碼,這有一些復(fù)雜,我們把和上邊類定義中重復(fù)的地方去掉,只留下繼承的實(shí)現(xiàn)部分


  var Gadget, IPhone,
    __extends = function(child, parent) { 
      for (var key in parent) { 
        if ({}.hasOwnProperty.call(parent, key)) 
          child[key] = parent[key]; 
      } 


      function ctor() { this.constructor = child; } 


      ctor.prototype = parent.prototype; 
      child.prototype = new ctor; 
      child.__super__ = parent.prototype; 


      return child; 
    };


  IPhone = (function(_super) {


    __extends(IPhone, _super);


    IPhone.name = 'IPhone';


    function IPhone() {
      this.nosell = __bind(this.nosell, this);
      IPhone.__super__.constructor.call(this, "iphone");
    }


    IPhone.prototype.nosell = function() {
      return "Don't " + (this.sell());
    };


    return IPhone;


  })(Gadget);


這里重點(diǎn)有三個(gè),

__extends函數(shù)使用了代理構(gòu)造函數(shù)ctor來(lái)實(shí)現(xiàn)繼承,這是非常普遍的js中對(duì)象繼承的實(shí)踐模式,進(jìn)一步解釋一下
使用代理構(gòu)造函數(shù)的目的是為了避免子類被更改時(shí)父類受到影響
使用ctor.prototype = parent.prototype的意義是只繼承定義在prototype上的公用屬性
父類的類成員被直接引用拷貝到子類,而不是原型繼承
super的實(shí)現(xiàn)方法是parent.prototype.constructor.call(this)

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)