Beautiful Mixins -《Beautiful JavaScript》讀書分享

剛開始,代碼只是代碼而已。當代碼多了,咱們發明了函數,以便代碼能夠重用。到後來,咱們的函數也愈來愈多,因此咱們又發明了別的一些編程概念。可是當咱們沉迷於這些新概念的同時,咱們可能就錯過了一些近在眼前的美麗事物……git

類繼承

類概念主要存在於面向對象編程中的,可能你們上學時都學過,好比Java、C#、Objective-C等面嚮對象語言。每一個實體都須要先要有一個類,這個類對於咱們來講是比較抽象的(不是說abstract class哦),而後子類還能夠繼承父類。程序員

這感受彷佛有點亞里士多德形而上學和分類法的味道,但簡單來講這就是面向對象的基礎,在寫出實際代碼以前,你得先寫出一些類、接口,而後類繼承類、類實現接口……github

實際上咱們人類並非很是擅長分類,打個比方,一個按鈕(Button),它應該是一個矩形(Rectangle),仍是一個控件(Control)呢?咱們可讓Button繼承Rectangle,讓Rectangle繼承Control……等等,是否是有什麼不對?express

因此,面向對象的概念可能會在項目剛開始時就把你壓垮,但這並非說面向對象就很差,只是有時候並不適合你的項目。編程

原型鏈繼承

可能你們都知道,JavaScript是不支持class關鍵字的,由於JavaScript自己就不做爲一個面嚮對象語言來設計,但並不妨礙你們想出各類招來實現classapi

JavaScript是基於原型鏈(Prototype)繼承的,可是其詭異的寫法可能讓好多初學者望而卻步。微信

function Circle() {
  this.radius = 7;
}
Circle.prototype = {
  area: function () {
    return Math.PI * this.radius * this.radius;
  },
  grow: function () {
    this.radius++;
  },
  shrink: function () {
    this.radius--;
  }
};

在ES5以後,咱們有了Object.create方法,好理解了一些。閉包

var circle = Object.create({
  area: function () {
    return Math.PI * this.radius * this.radius;
  },
  grow: function () {
    this.radius++;
  },
  shrink: function () {
    this.radius--;
  }
}, {
  radius: {
    writable: true,
    configurable: true,
    value: 7
  }
});

只是第二參數用起來有點複雜,其實就是跟Object.defineProperties()的參數同樣,可是咱們如今又有了ES6呀:app

class Cicle {
  constructor() {
    this.radius = 7;
  }
  area() {
    return Math.PI * this.radius * this.radius;
  }
  grow() {
    this.radius++;
  }
  shrink() {
    this.radius--;
  }
}

這都是一些使用JavaScript實現class的例子,實際上,JavaScript還能夠有另一種重用代碼的方式——Mixins。框架

Mixins

這就是咱們今天的主題,近在眼前的美麗事物,但可不是什麼新概念,用過Ruby或Python的同窗,可能有所耳目,Mixins的字面意思就是把東西摻合在一塊兒。

在JavaScript咱們有callapply,很容易切換上下文,將各類互不相干糅合,達到Mixins的目的。

假設咱們要作一個圓形按鈕(RoundButton),它有兩個特性:

  1. 是圓的

  2. 可點擊

咱們能夠把這兩個特性分別寫作2個函數:

// 是圓的
var withCircle = function () {
  this.area = function () {
    return Math.PI * this.radius * this.radius;
  };
  this.grow = function () {
    this.radius++;
  };
  this.shrink = function () {
    this.radius--;
  }
}

// 可點擊
var withClickable = function () {
  this.hover = function () {
    console.log('hovering');
  };
  this.press = function () {
    console.log('button pressed');
  };
  this.release = function () {
    console.log('button released');
  };
  this.fire = function () {
    this.action.fire();
  };
}

這是咱們的圓形按鈕:

var RoundButton = function(radius, label, action) {
  this.radius = radius;
  this.label = label;
  this.action = action;
};

如今咱們要讓給這個圓形按鈕附上那兩個特性:

withCircle.call(RoundButton.prototype);
withClickable.call(RoundButton.prototype);

var button = new RoundButton(4, 'yes!', function() {
  return 'you said yes!'
});
button1.fire(); // 輸出 'you said yes!'

這樣寫,是否是瞬間顯得既簡潔又天然?讓人一眼看懂代碼在作什麼。

固然這些附加特性的函數用的多了,也就建立了許多函數,這裏能夠簡單的用一個當即執行函數(Immediately Invoked Function Expression)的閉包來對其進行優化一下,以withCircle爲例:

var withCircle = (function () {
  function area() {
    return Math.PI * this.radius * this.radius;
  }
  function grow() {
    this.radius++;
  }
  function shrink() {
    this.radius--;
  }
  return function () {
    this.area = area;
    this.grow = grow;
    this.shrink = shrink;
  };
})();

這樣就不須要每次使用都新建函數了,從而節省更多的資源。

Advice

有時候,你沒法確保某些函數可能會覆蓋原有的功能,例如如下例子:

Button.prototype.press = function() {
  console.log('pressed');
};

// 這時再用咱們的 withClickable,就會覆蓋掉 press
withClickable.call(Button.prototype);

這時候咱們應該採用Advice,Twitter的Flight框架已經提供了此功能:

var withClickable = function () {
  this.after('press', function () {
    console.log('press again.');
  });
};

withAdvice.call(Button.prototype);
withClickable.call(Button.prototype);

var button = new Button();
button.press(); // 輸出 'pressed', 'press again.'

兩個press並不會互相沖突,而是有前後順序的執行,就相似經過addEventListener添加了多個事件而不是直接修改onclick同樣,具體細節能夠參考Flight的API。

小結

做爲一名程序員,咱們或許在上學時就被灌輸了面向對象的固有思想,畢竟面向對象從上世紀90年代到如今,經久不衰,自由它的優點。可是在JavaScript中,若是你並不善於面向對象的抽象思惟,何不嘗試一下Mixins呢?並且Mixins與類繼承相比,還能更好的解耦合,能夠用於任何Object之上,正好用上了JavaScript若類型的優點。

最近在讀《Beautiful JavaScript》這本書,有一些好的內容,正好能夠跟你們分享,但並非所有,有興趣的同窗也能夠本身讀一下,請支持正版。

原文連接:http://t.cn/RteECIF
微信號:程序員晉級之路『code-learning』

clipboard.png

相關文章
相關標籤/搜索