摘要:链式调用精髓在于重用一个初始操作可以把方法的链式调用技术写到自己所写的整个库中把自己喜欢的方法串起来调用两个部分一个创建代表元素的对象的工厂还有一批对这个元素执行某些操作的方法每一个这种方法都可以在方法名前附加一个圆点后加入调用链中方法的链
链式调用 精髓在于重用一个初始操作.
addEvent($(".example"), "click", function () { // Without chaining: $(this).hide(); setStyle(this, "color", "green"); $(this).show(); // With chaining; $(this).hide().setStyle("color", "green").show(); });调用链结构
$函数通常会返回一个HTML 元素(的集合):
function $() { var eles = []; for (var i = 0, len = arguments.length; i < len; ++i) { var ele = arguments[i]; if (typeof ele === "string") { ele = document.getElementById(ele); } if (arguments.length === 1) { return ele; } eles.push(ele); } return eles; }
如果把这个函数改造成一个构造器,把那些元素作为数组保存在一个实例属性中,并让所有定义在构造器函数的 prototype 属性所指对象中的方法都返回用来调用方法的那个实例的引用,那么它就具有链式调用的能力.
做一下改进:首先把$函数改成一个工厂方法,负责创建支持链式调用的对象,这个函数应该能接受元素数组形式的参数,所以我们能够使用和原来一样的公有接口:
(function () { // Use a private class. function _$(els) { this.eles = []; for (var i = 0, len = els.length; i < len; ++i) { var ele = els[i]; if (typeod ele === "string") { ele = document.getElementById(ele); } this.eles.push(ele); } } // The public interface remains the same. window.$ = function () { return new _$(arguments); }; })();
由于所有对象都会继承其原型对象的属性和方法,所以我们可以让定义在原型对象中的那几个方法都返回用以调用方法的实例对象的引用,这样就可以对哪些方法进行链式调用.现在在_$这个私有构造函数的 prototype 对象中添加方法:
(function () { function _$(eles) { // ... } _$.prototype = { each: function (fn) { for (var i = 0, len = this.eles.length; i < len; ++i) { fn.call(this, this.eles[i]); } return this; }, hide: function (0 { var that = this; this.setStyle("display", "none"); }); setStyle: function (prop, val) { this.each(function (ele) { ele.style[prop] = val; }); return this; }, show: function (0 { var that = this; this.setStyle("display", "block"); }); return this; addEvent: function(type, fn) { var add = function (ele) { if (window.addEventListener) { ele.addEventList(type, fn, false); } else if (window.attachEvent) { ele.attachEvnet("on" + type, fn); } }; this.each(function (el) { add(el); }); return this; } }; window.$ = function () { return new _$(arguments); }; })();
每个方法的最后一行return this;会讲调用方法对象传给调用链上的下一个方法.
jQuery 便是这样,window 对象或者某个 HTML 元素是调用链的锚点,多有操作都挂系在上面.
链式调用很适合于赋值器方法,但是对于取值器方法,并不希望方法返回 this.不过使用回调技术可以返回你想要的数据而不是 this.
// Accessor without function callbacks: returning requested data in accessors. window.API = window.API || function () { var name = "Hello world"; // Privilleged mutator this.setName = function(newName) { name = newName; return this; }; // Privileged accessor method. this.getName = function () { return name; }; }; // Implementation code var o = new API; console.log(o.getName()); // Displays "Hello world". console.log(o.setName("nanci").getName()); // Display "nanci" // Accessor with function callbacks. window.API2 = window.API2 || function () { var name = "Hello world"; // Privilleged mutator this.setName = function(newName) { name = newName; return this; }; // Privileged accessor method. this.getName = function (callback) { callback.call(this, name); return this; }; } // Implementation code var o2 = new API2; o2.getName(console.log).setName("nanci").getName(console.log); // Displays "Hello world" and then display "nanci"小结
使用链式调用可以避免多次重复使用一个对象变量,减少代码量.
如果想让类的接口保持一致,让取值器像赋值器那样也支持链式调用,那么可以使用回调.
----------another part----------
工厂如果你想开几个自行车商店,每个店都有几种型号的自行车出售,用一个类表示:
// BicycleShop class. var BicycleShop = function () {}; BicycleShop.prototype = { sellBicycle: function(model) { var bicycle; switch(model) { case "The Speedter": bicycle = new Speedter(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Comfort Cruiser": default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); bicycle.assemble(); bicycle.wash(); return bicycle; } };
sellBicycle 方法根据所要求的自行车型号用 switch 语句创建一个自行车的实例.各种型号的自行车实例可以互换使用,因为他们都实现了 Bicycle 接口(接口在工厂中很重要,如果不对对象进行某种类型检查以其确保其实现了必须的方法,那么工厂模式并不能带来什么好处).
// The Bicycle interface. var Bicycle = new Interface("Bicycle", ["assemble", "wash", "ride", "repair"]); // Speedster class. var Speedster = function () { // implement Bicycle ... }; Speedster.prototype = { assemble: function () { ... }, wash: function () { ... }, ride: function () { ... }, repair: function () { ... } };
要出售某种型号自行车,只需要调用 sellBicycle 方法即可:
var californiaCruisers = new BicycleShop(); var yourNewBike = californiaCruisers.sellBicycle("The Speedster");
如果你想在供货目录中加入一款新车型,更好的解决办法是把 sellBicycle 方法中"创建新实例"这部分工作转交给一个简单工厂对象.
// BicycleFactory namespace. var BicycleFactory = { createBicycle: function (model) { var bicycle; switch(model) { case "The Speedter": bicycle = new Speedter(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Comfort Cruiser": default: bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; } };
BicycleFactory 是一个单体,用来把 createBicycle 方法封装在一个命名空间中,这个方法返回一个实现了 Bicycle接口的对象,然后可以对其进行组装和清洗:
// BicycleShop class, improved. var BicycleShop = function () {}; BicycleShop.prototype = { sellBicycle: function (model) { var bicycle = BicycleFactory.createBicycle(model); bicycle.assemble(); bicycle.wash(); return bicycle; } };
这个 BicycleFactory 对象可以供各种类用来创建新的自行车实例.有关可供车型的所有信息都集中在一个地方管理,所以添加更多车型很容易:
// BicycleFactory namespace, with more models. var Bicycle: function (model) { var bicycle; switch (model) { case "The Speedster": bicycle = new Speedster(); break; case "The Lowrider": bicycle = new Lowrider(); break; case "The Flatlander": bicycle = new Flatlander(); break; case "The ComfortCruiser": bicycle = new ComfortCruiser(); } Interface.ensureImplements(bicycle, Bicycle); return bicycle; }
这是一个简单工厂的例子,他把成员对象的创建工作交给一个外部对象,这个外部对象可以是一个简单的命名空间,也可以是一个类的实例.
示例: XHR 工厂用 Ajax 技术发起异步请求是现在 Web 开发的一个常见任务.用于发起请求的对象是某种类的实例,具体是哪种类取决于用户的浏览器.如果代码中需要多次执行 ajax 请求,那么可以把创建这种对象的代码提取到一个类中,并创建一个包装器来包装在实际发起请求时所要经历的一系列步骤,简单工厂非常适合该场合,根据浏览器特性生成一个 XMLHttpRequest 或者 ActiveXObject 实例.
// AjaxHandler interface. var AjaxHandler = new Interface("AjaxHandler", ["request", "createXhrObject"]); // SimpleHandler class. var SimleHandler = function () {}; // implements AjaxHandler SimpleHandler.prototype = { request: function (method, url, callback, postVars) { var xhr = this.createXhrObject(); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; (xhr.status === 200) ? callback.success(xhr.responseText) : callback.failure(xhr.status); }; xhr.open(method, url, true); if (method !== "POST") { postVars = null; } xhr.send(postVars); }, createXhrObject: function () { // Factory method. var methods = [ function () { return newXMLHttpRequest(); }, function () { return new ActiveXObject("Msxml2.XMLHTTP"); }, function () { return new ActiveXObject("Microsoft.XMLHTTP"); } ]; for (var i =0, len = methods.length; i < len; i++) { try { methods[i](); } catch(e) { continue; } //If we reach this point, method[i] worked. this.createXhrObject = methods[i]; //Memoize the method. return methods[i]; } // If we reach this point, none of the methods worked. throw new Error("SimpleHandler: Could not create an XHR object."); } }利
主要好处在于消除对象间的耦合,通过使用工厂方法而不是 new 关键字及具体类,你可以把所有实例化代码集中在一个位置.可以大大简化更换所用的类或者在运行期间动态选择所有的类的工作.在派生子类时也更灵活.可以先创建一个抽象的超类,然后在子类中创建工厂方法,从而把成员对象的实例化推迟到更专门化的子类中进行.
所有这些好处都和面向对象设计的两条原则相关: 弱化对象间的耦合:防止代码的重复.在一个方法中进行类的实例化,可以消除重复性的代码.这是在用一个对接口的调用取代一个具体的实现.这些都有助于模块化代码.
不能把工厂方法当万金油,而把普通函数扔在以便.如果根本不可能另外换用一个类或者不需要在运行期间在一系列类的选择,那么就不应该使用工厂方法.大多数最好使用 new 关键字和构造函数公开进行实例化,这样代码会更简单易读..一眼就看到调用的构造函数,不必去查看某个工厂方法去知道实例化的是什么类.
小结文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/87903.html
之前也有和大家讲过有关JS的对象创建和对象继承,本篇文章主要为大家做个汇总和梳理。 JS中其实就是原型链继承和构造函数继承的毛病,还有就是工厂、构造、原型设计模式与JS继承。 JS高级程序设计4:class继承的重点,不只是简简单单的语法而已。 对象创建 不难发现,每一篇都离不开工厂、构造、原型这3种设计模式中的至少其一! 那JS为什么非要用到这种3种设计模式了呢?? 我们先从对...
原文中详细的讲解了Pytest架构之fixture,原文中根据实例编码推荐的十分详尽。对大家学习培训和工作具有很强的参阅参考意义,需用的小伙伴可以参考一下 原文中有关fixture的具体内容如下所示: 1、参数化设计fixture 2、fixture工厂 3、request这一fixture 1、参数化设计fixture fixture有个params主要参数,容许大家传送数据。 ...
摘要:第二个调用当前执行的函数,并为其设置另外一个定时器。使得在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。 在自己用canvas画一个时钟时,画秒钟用的是利用图片将重复的线条遮住,但是会出现有两个秒钟线条同时存在,才想起setInterval有那么个坑,查了点资料,记录下,若有不对的或者未写到的点,还请大家指出,谢谢^_^ 在此之前先科普下这个学习点...
阅读 1810·2021-11-24 09:39
阅读 2294·2021-09-30 09:47
阅读 4153·2021-09-22 15:57
阅读 1880·2019-08-29 18:36
阅读 3580·2019-08-29 12:21
阅读 595·2019-08-29 12:17
阅读 1270·2019-08-29 11:25
阅读 729·2019-08-28 18:26