委托

[[Prototype]] 机制是一种存在于一个对象上的内部链接,它指向一个其他对象。

作为与面向类(也就是,OO —— 面向对象)的对比,我称这种风格的代码为 “OLOO”(objects-linked-to-other-objects(链接到其他对象的对象))

在 JavaScript 中,[[Prototype]] 机制将 对象 链接到其他 对象。无论你多么想说服自己这不是真的,JavaScript 没有像“类”那样的抽象机制。

行为委托 意味着:在某个对象(XYZ)的属性或方法没能在这个对象(XYZ)上找到时,让这个对象(XYZ)为属性或方法引用提供一个委托(Task)。

image.png

我们唯一需要真正关心的事情是 链接到其他对象的对象
**

Classes

  1. class Widget {
  2. constructor(width,height) {
  3. this.width = width || 50;
  4. this.height = height || 50;
  5. this.$elem = null;
  6. }
  7. render($where){
  8. if (this.$elem) {
  9. this.$elem.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.$elem = $( "<button>" ).text( this.label );
  21. }
  22. render($where) {
  23. super.render( $where );
  24. this.$elem.click( this.onClick.bind( this ) );
  25. }
  26. onClick(evt) {
  27. console.log( "Button '" + this.label + "' clicked!" );
  28. }
  29. }

除了语法上 看起来 更好,ES6 还解决了什么?

  1. 不再有(某种意义上的,继续往下看!)指向 .prototype 的引用来弄乱代码。
  2. Button 被声明为直接“继承自”(也就是 extendsWidget,而不是需要用 Object.create(..) 来替换 .prototype 链接的对象,或者用 __proto__Object.setPrototypeOf(..) 来设置它。
  3. super(..) 现在给了我们非常有用的 相对多态 的能力,所以在链条上某一个层级上的任何方法,可以引用链条上相对上一层的同名方法。第四章中有一个关于构造器的奇怪现象:构造器不属于它们的类,而且因此与类没有联系。super(..) 含有一个对此问题的解决方法 —— super() 会在构造器内部想如你期望的那样工作。
  4. class 字面语法对指定属性没有什么启发(仅对方法有)。这看起来限制了某些东西,但是绝大多数情况下期望一个属性(状态)存在于链条末端的“实例”以外的地方,这通常是一个错误和令人诧异(因为这个状态被隐含地在所有“实例”中“分享”)的。所以,也可以说 class 语法防止你出现错误。
  5. extends 甚至允许你用非常自然的方式扩展内建的对象(子)类型,比如 Array 或者 RegExp。在没有 class .. extends 的情况下这样做一直以来是一个极端复杂而令人沮丧的任务,只有最熟练的框架作者曾经正确地解决过这个问题。

class 的坑

首先,class 语法可能会说服你 JS 在 ES6 中存在一个新的“类”机制。但不是这样。 class 很大程度上仅仅是一个既存的 [[Prototype]](委托)机制的语法糖!

这意味着 class 实际上不是像传统面向类的语言那样,在声明时静态地拷贝定义。如果你在“父类”上更改/替换了一个方法(有意或无意地),子“类”和/或实例将会受到“影响”,因为它们在声明时没有得到一份拷贝,它们依然都使用那个基于 [[Prototype]] 的实时委托模型。