面向委托的设计

假设软件中建模设计一些任务(“XYZ”,“ABC”等)。
首先定义一个名为Task的对象,它会包含所有任务都可以使用的具体行为。接着,对于每个任务(“XYZ”,“ABC”)都会定义一个对象来存储对应的数据和行为。开发者会把特定的任务对象都关联到Task功能对象,让它们在需要的时候可以进行委托。

  1. let Task = {
  2. setID: function(ID){ this.id = ID; },
  3. outputID: function(){ console.log(this.id); }
  4. }
  5. // 让XYZ委托Task
  6. let XYZ = Object.create(Task);
  7. XYZ.prepareTask = function(ID,label){
  8. this.setID(ID);
  9. this.label = label;
  10. };
  11. XYZ.outputTaskDetails = function(){
  12. this.outputID();
  13. console.log(this.label);
  14. }

在JavaScript中,[[Prototype]]原型链机制会把对象关联其他对象。无论怎么说,JavaScript中就是没有类似“类”的抽象机制。
委托行为意味着某些对象(XYZ)在找不到属性或者方法引用是会把这个请求委托给另一个对象(Task)。

ES6中的Class

现在我们来看看ES6的class机制,首先我们看下面的例子。

  1. class Widget{
  2. constructor(width,height){
  3. this.width = width || 50;
  4. this.height = height || 50;
  5. this.$element = null;
  6. }
  7. render($where){
  8. if(this.$element){
  9. this.$element.css({
  10. width: this.width + 'px',
  11. height: this.height + 'px',
  12. }).appendTo($where);
  13. }
  14. }
  15. }
  16. class Button extends Widget{
  17. constructor(width,height,label){
  18. super(width,height);
  19. this.label = label || 'Default';
  20. this.$element = $('button').text(this.label);
  21. }
  22. render($where){
  23. super.render($where);
  24. this.$element.click(this.onClick.bind(this));
  25. }
  26. onClick(evt){
  27. console.log("Button" + this.label + " clicked");
  28. }
  29. }

ES6的class引入,使开发者不再引用杂乱的.prototype,也可以通过super实现相对多态,这样任何方法都可以引用原型链上层的同名方法。
可以通过extends很自然的扩展对象子类型。
ES6的class语法是向JavaScript中引入了全新的“类”机制,其实不是这样,class基本上只是现有的[[Prototype]]机制的一种语法糖。

其实我们还是可以在class机制中使用.prototype

  1. class C {
  2. constructor(){
  3. C.prototype.count++;
  4. console.log("Hello: " + this.count);
  5. }
  6. }
  7. C.prototype.count = 0;
  8. let c1 = new C(); // Hello: 1
  9. let c2 = new C(); // Hello: 2
  10. c1.count === 2; // true
  11. c1.count === c2.count; // true