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“继承”WidgetButton.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标识符,这会导致:
- 调用栈更难追踪;
- 自我引用(递归,事件的绑定解除等等)更难;
- 代码(稍微)更难理解;
简洁语法方式无法避免的是第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;// trueObject.getPrototypeOf(Bar.prototype) === Foo.prototype; //trueFoo.prototype.isPrototypeOf(Bar.prototype);// true// 让b1关联到Foo和Barb1 instanceof Foo;// trueb1 instanceof Bar;// trueObject.getPrototypeOf(b1) === Bar.prototype; //trueFoo.isPrototypeOf(b1);// trueBar.isPrototypeOf(b1);// true
上面👆这种做法,很容易人理解成是继承关系,它们只不过是关联关系而已,但是也只能这样子
还有一种更加脆弱的内省模式,但是在开发者上面用的很多
if(a1.something){a1.somethinf();}
这种方法就是,例如需要判断一个对象引用是否是Promise,但是判断的方法是检查对象是否有then()的方法,就可以判断它具有Promise的所有标准行为,但是也有一定的危险性
