刚开始,代码只是代码而已。当代码多了,咱们发明了函数,以便代码能够重用。到后来,咱们的函数也愈来愈多,因此咱们又发明了别的一些编程概念。可是当咱们沉迷于这些新概念的同时,咱们可能就错过了一些近在眼前的美丽事物……git
类概念主要存在于面向对象编程中的,可能你们上学时都学过,好比Java、C#、Objective-C等面向对象语言。每一个实体都须要先要有一个类,这个类对于咱们来讲是比较抽象的(不是说abstract class哦),而后子类还能够继承父类。程序员
这感受彷佛有点亚里士多德形而上学和分类法的味道,但简单来讲这就是面向对象的基础,在写出实际代码以前,你得先写出一些类、接口,而后类继承类、类实现接口……github
实际上咱们人类并非很是擅长分类,打个比方,一个按钮(Button),它应该是一个矩形(Rectangle),仍是一个控件(Control)呢?咱们可让Button继承Rectangle,让Rectangle继承Control……等等,是否是有什么不对?express
因此,面向对象的概念可能会在项目刚开始时就把你压垮,但这并非说面向对象就很差,只是有时候并不适合你的项目。编程
可能你们都知道,JavaScript是不支持class
关键字的,由于JavaScript自己就不做为一个面向对象语言来设计,但并不妨碍你们想出各类招来实现class
。api
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。框架
这就是咱们今天的主题,近在眼前的美丽事物,但可不是什么新概念,用过Ruby或Python的同窗,可能有所耳目,Mixins的字面意思就是把东西掺合在一块儿。
在JavaScript咱们有call
和apply
,很容易切换上下文,将各类互不相干糅合,达到Mixins的目的。
假设咱们要作一个圆形按钮(RoundButton),它有两个特性:
是圆的
可点击
咱们能够把这两个特性分别写作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; }; })();
这样就不须要每次使用都新建函数了,从而节省更多的资源。
有时候,你没法确保某些函数可能会覆盖原有的功能,例如如下例子:
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』