JavaScript基础-行为委托

207 阅读3分钟

JavaScript基础-行为委托

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

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

1. 类与对象

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

// 父类
// 定义内部属性
function Widget(width, height){
	this.width = width || 50;
  this.height = height || 50;
  this.$elem = null;
}
// 渲染的方法
Widget.prototype.render = function($where) {
	if(this.$elem){
    // 样式
  	this.$elem.css({
    	width: `${this.width}px`,
      height: `${this.height}px`
      // 放到哪个位置上
    }).appendTo($where)
  }
}
// 子类
function Button(width,height,label) {
	// 调用“super”构造函数
  // 丑陋的显式伪多态👇
  Widget.call(this, width, height);
  this.label = label || "Default";
  this.$elem = $("<button>").text(this.label);
}
// 让Button“继承”Widget
Button.prototype = Object.create(Widget.prototype);
// 重写render(...)
Button.prototype.render = function($where){
	// “super”调用
  // 丑陋的显式伪多态👇,从子类方法中引用父类的基础方法,呸!!丑陋
  Widget.prototype.call(this, $where);
  this.$elem.click(this.onclick.bind(this)); // 绑定
}
Button.prototype.onclick = function(evt){
	console.log(`Button ${this.label} clicked!`)
}
$(document).ready(function(){
	var $body = $(document.body);
  var btn1 = new Button(125, 30, "Hello");
  var btn2 = new Button(150, 40, "world");
  btn1.render($body);
  btn2.render($body);
})


ES6的class语法糖🍬

class Widget{
	constructor(width, height){
  	this.width = width || 50;
  	this.height = height || 50;
    this.$elem = null;
  }
  render($where){
  	if(this.$elem){
    // 样式
  	this.$elem.css({
    	width: `${this.width}px`,
      height: `${this.height}px`
      // 放到哪个位置上
    }).appendTo($where)
  	}
  }
}

class Button extends Widget {
	constructor(width,height,label){
  	super(width,height);
    this.label = label || "Default";
    this.$elem = $("<button>").text(this.label);
  }
  render($where){
  	super.render($where);
    this.$elem.click(this.onclick.bind(this)); // 绑定
  }
  onclick(evt){
		console.log(`Button ${this.label} clicked!`)
	}
}
$(document).ready(function(){
	var $body = $(document.body);
  var btn1 = new Button(125, 30, "Hello");
  var btn2 = new Button(150, 40, "world");
  btn1.render($body);
  btn2.render($body);
})

委托设计模式的

var Widget = {
	init:function(width,height){
  	this.width = width || 50;
  	this.height = height || 50;
    this.$elem = null;
  },
  insert:function($where){
  	if(this.$elem){
      // 样式
      this.$elem.css({
        width: `${this.width}px`,
        height: `${this.height}px`
        // 放到哪个位置上
      }).appendTo($where)
  	}
  }
}
var Button = Object.create(Widget);
Button.setup = function(width, height) {
	// 委托调用
  this.init(width, height);
 	this.label = label || "Default";
  this.$elem = $("<button>").text(this.label);
}
Button.build = function($where){
	// 委托调用
  this.insert($where);
 	this.$elem.click(this.onclick.bind(this));
}
Button.click = function(evt){
	console.log(`Button ${this.label} clicked!`)
} 
$(document).ready(function(){
	var $body = $(document.body);
  var btn1 = Object.create(Button);
  btn1.setup(125,30,"hello");
  var btn2 = Object.create(Button);
  btn2.setup(130,40,"world");
  
  btn1.build($body);
  btn2.build($body);
})

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

2. 更好的语法

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

class Foo(){
 getUser() {/*...*/}
}

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

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

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


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

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

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

3. 内省

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

function Foo(){/*...*/}
Foo.prototype...

function Bar(){/*...*/}
Bar.prototype = Object.create(Foo.prototype);
var b1 = new Bar("b1");

// 让Foo和Bar互相关联 
Bar.prototype instanceof Foo;// true
Object.getPrototypeOf(Bar.prototype) === Foo.prototype; //true
Foo.prototype.isPrototypeOf(Bar.prototype);// true

// 让b1关联到Foo和Bar
b1 instanceof Foo;// true
b1 instanceof Bar;// true
Object.getPrototypeOf(b1) === Bar.prototype; //true
Foo.isPrototypeOf(b1);// true
Bar.isPrototypeOf(b1);// true

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

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

if(a1.something){	
	a1.somethinf();   
}

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