JavaScript中的原型链的机制就是对象之间的关联关系,然后就存在了行为委托的说法

委托行为就意味着某些对象在找不到属性或者方法的情况下,会把这个请求委托给另外一个对象

1. 类与对象

纯JavaScript风格实现类风格的代码,将使用jQuery来操作dom和css

  1. // 父类
  2. // 定义内部属性
  3. function Widget(width, height){
  4. this.width = width || 50;
  5. this.height = height || 50;
  6. this.$elem = null;
  7. }
  8. // 渲染的方法
  9. Widget.prototype.render = function($where) {
  10. if(this.$elem){
  11. // 样式
  12. this.$elem.css({
  13. width: `${this.width}px`,
  14. height: `${this.height}px`
  15. // 放到哪个位置上
  16. }).appendTo($where)
  17. }
  18. }
  19. // 子类
  20. function Button(width,height,label) {
  21. // 调用“super”构造函数
  22. // 丑陋的显式伪多态👇
  23. Widget.call(this, width, height);
  24. this.label = label || "Default";
  25. this.$elem = $("<button>").text(this.label);
  26. }
  27. // 让Button“继承”Widget
  28. Button.prototype = Object.create(Widget.prototype);
  29. // 重写render(...)
  30. Button.prototype.render = function($where){
  31. // “super”调用
  32. // 丑陋的显式伪多态👇,从子类方法中引用父类的基础方法,呸!!丑陋
  33. Widget.prototype.call(this, $where);
  34. this.$elem.click(this.onclick.bind(this)); // 绑定
  35. }
  36. Button.prototype.onclick = function(evt){
  37. console.log(`Button ${this.label} clicked!`)
  38. }
  39. $(document).ready(function(){
  40. var $body = $(document.body);
  41. var btn1 = new Button(125, 30, "Hello");
  42. var btn2 = new Button(150, 40, "world");
  43. btn1.render($body);
  44. btn2.render($body);
  45. })

ES6的class语法糖🍬

  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. // 样式
  10. this.$elem.css({
  11. width: `${this.width}px`,
  12. height: `${this.height}px`
  13. // 放到哪个位置上
  14. }).appendTo($where)
  15. }
  16. }
  17. }
  18. class Button extends Widget {
  19. constructor(width,height,label){
  20. super(width,height);
  21. this.label = label || "Default";
  22. this.$elem = $("<button>").text(this.label);
  23. }
  24. render($where){
  25. super.render($where);
  26. this.$elem.click(this.onclick.bind(this)); // 绑定
  27. }
  28. onclick(evt){
  29. console.log(`Button ${this.label} clicked!`)
  30. }
  31. }
  32. $(document).ready(function(){
  33. var $body = $(document.body);
  34. var btn1 = new Button(125, 30, "Hello");
  35. var btn2 = new Button(150, 40, "world");
  36. btn1.render($body);
  37. btn2.render($body);
  38. })

委托设计模式的

  1. var Widget = {
  2. init:function(width,height){
  3. this.width = width || 50;
  4. this.height = height || 50;
  5. this.$elem = null;
  6. },
  7. insert:function($where){
  8. if(this.$elem){
  9. // 样式
  10. this.$elem.css({
  11. width: `${this.width}px`,
  12. height: `${this.height}px`
  13. // 放到哪个位置上
  14. }).appendTo($where)
  15. }
  16. }
  17. }
  18. var Button = Object.create(Widget);
  19. Button.setup = function(width, height) {
  20. // 委托调用
  21. this.init(width, height);
  22. this.label = label || "Default";
  23. this.$elem = $("<button>").text(this.label);
  24. }
  25. Button.build = function($where){
  26. // 委托调用
  27. this.insert($where);
  28. this.$elem.click(this.onclick.bind(this));
  29. }
  30. Button.click = function(evt){
  31. console.log(`Button ${this.label} clicked!`)
  32. }
  33. $(document).ready(function(){
  34. var $body = $(document.body);
  35. var btn1 = Object.create(Button);
  36. btn1.setup(125,30,"hello");
  37. var btn2 = Object.create(Button);
  38. btn2.setup(130,40,"world");
  39. btn1.build($body);
  40. btn2.build($body);
  41. })

在委托设计模式中,这种对象关联风格代码相比传统原型风格代码有优势的地方,使用类构造函数的话,你需要在同一步骤中实现构造和初始化,然而许多情况把这两步分开像对象关联代码一样更灵活

2. 更好的语法

使用class语法糖,还有简洁方式声明

  1. class Foo(){
  2. getUser() {/*...*/}
  3. }

简洁语法实际上会变成一个匿名函数表达式并赋值给getUser,(function(…){})

一般匿名函数没有name标识符,这会导致:

  1. 调用栈更难追踪;
  2. 自我引用(递归,事件的绑定解除等等)更难;
  3. 代码(稍微)更难理解;

简洁语法方式无法避免的是第2点👆,其他的话,因为是特殊性,也会有给函数对象设置一个内部的name属性

如果需要自我引用的话,就要使用具名函数表达式了

  1. var Foo = {
  2. bar: function(x){
  3. if(x<10){
  4. return Foo.bar(x*2);
  5. }
  6. return x;
  7. },
  8. // 当多个对象通过代理共享函数,使用this绑定的话,就要使用下面👇的name标识符来真正引用
  9. baz: function baz() {
  10. if(x) {
  11. return baz(x*2)
  12. }
  13. return x;
  14. }
  15. }

3. 内省

内省就是检查实例的类型,主要目的是通过创建方式来判断对象的结构和功能

  1. function Foo(){/*...*/}
  2. Foo.prototype...
  3. function Bar(){/*...*/}
  4. Bar.prototype = Object.create(Foo.prototype);
  5. var b1 = new Bar("b1");
  6. // 让Foo和Bar互相关联
  7. Bar.prototype instanceof Foo;// true
  8. Object.getPrototypeOf(Bar.prototype) === Foo.prototype; //true
  9. Foo.prototype.isPrototypeOf(Bar.prototype);// true
  10. // 让b1关联到Foo和Bar
  11. b1 instanceof Foo;// true
  12. b1 instanceof Bar;// true
  13. Object.getPrototypeOf(b1) === Bar.prototype; //true
  14. Foo.isPrototypeOf(b1);// true
  15. Bar.isPrototypeOf(b1);// true

上面👆这种做法,很容易人理解成是继承关系,它们只不过是关联关系而已,但是也只能这样子

还有一种更加脆弱的内省模式,但是在开发者上面用的很多

  1. if(a1.something){
  2. a1.somethinf();
  3. }

这种方法就是,例如需要判断一个对象引用是否是Promise,但是判断的方法是检查对象是否有then()的方法,就可以判断它具有Promise的所有标准行为,但是也有一定的危险性