1. Was ist ein Kettenruf?
Das ist zum Beispiel leicht zu verstehen:
$(this).setStyle('color', 'red').show();
Der Unterschied zwischen allgemeinen Funktionsaufrufen und Kettenaufrufen: Geben Sie nach dem Aufruf einer Methode das Objekt der aktuell aufrufenden Methode zurück.
function Dog(){ this.run= function(){ alert("The dog is running...."); return this;//返回当前对象 Dog }; this.eat= function(){ alert("After running the dog is eatting...."); return this;//返回当前对象 Dog }; this.sleep= function(){ alert("After eatting the dog is running...."); return this;//返回当前对象 Dog }; } //一般的调用方式; /* var dog1 =new Dog(); dog1.run(); dog1.eat(); dog1.sleep();*/ var dog2 = new Dog(); dog2.run().eat().sleep();
2. Kettenaufrufe zerlegen
Der Kettenruf besteht eigentlich aus zwei Teilen:
1). Operationsobjekt (d. h. das manipulierte DOM-Element, wie z. B. $(this) im obigen Beispiel)
2). Betriebsmethode (insbesondere was zu tun ist, wie z. B. setStyle und show im obigen Beispiel)
So implementieren Sie Operationsobjekte und Operationsmethoden
Erstellen Sie eine generische $-Funktion:
function $(){ var elements = []; for(var i= 0,len=arguments.length; i<len; i++){ var element = arguments[i]; if(typeof element==='string'){ element = document.getElementById(element); } if(arguments.length==1){ return element; } elements.push(element); } return elements; }
Wenn Sie diese Funktion jedoch in einen Konstruktor umwandeln, speichern Sie diese Elemente als Array in einem Instanzattribut und lassen Sie alle Methoden, die in dem Objekt definiert sind, auf das das Prototypattribut der Konstruktorfunktion zeigt, zurückkehren, um die Methode als Referenz aufzurufen zu dieser Instanz, dann hat es die Möglichkeit, Anrufe zu verketten. (Nachdem ich so viel gesagt habe, geben Sie dies einfach am Ende jeder Methode zurück),
Zuerst muss ich diese $-Funktion in eine Factory-Methode umwandeln, die für die Erstellung von Objekten verantwortlich ist, die Kettenaufrufe unterstützen. Diese Funktion sollte in der Lage sein, Argumente in Form eines Arrays von Elementen zu akzeptieren, damit wir dieselbe öffentliche Schnittstelle wie zuvor verwenden können. Auf diese Weise besteht die Möglichkeit, Kettenanrufe zu tätigen.
Die Transformation ist wie folgt:
(function(){ function _$(els){ this.elements = [];//把那些元素作为数组保存在一个实例属性中, for(var i= 0, len=els.length; i<len; i++){ var element = els[i]; if(typeof element==='string'){ element = document.getElementById(element); } this.elements.push(element); } } _$.prototype = { each: function(fn){ for(var i= 0,len=this.elements.length; i<len; i++){ fn.call(this, this.elements[i]); } return this; //在每个方法的最后return this; }, setStyle: function(prop, val){ this.each(function(el){ el.style[prop] = val; }); return this; //在每个方法的最后return this; }, show: function(){ var that = this; this.each(function(el){ that.setStyle('display', 'block'); }); return this; //在每个方法的最后return this; }, addEvent: function(type, fn){ var add = function(el){ if(window.addEventListener){ el.addEventListener(type, fn, false); }else if(window.attachEvent){ el.addEvent('on'+type, fn); } }; this.each(function(el){ add(el); }); return this; //在每个方法的最后return this; } } window.$ = function(){ return new _$(arguments); } })();
Gib dies am Ende zurück, wodurch das Objekt der aufrufenden Methode an die nächste Methode in der Aufrufkette übergeben wird.
3. Simulieren Sie die zugrunde liegende Kettenprogrammierung von JQuery
// 块级作用域 //特点1 程序启动的时候 里面的代码直接执行了 //特点2 内部的成员变量 外部无法去访问 (除了不加var修饰的变量) (function(window , undefined){ // $ 最常用的对象 返回给外界 大型程序开发 一般使用'_'作为私用的对象(规范) function _$(arguments){ //实现代码...这里仅实现ID选择器 // 正则表达式匹配id选择器 var idselector = /#\w+/ ; this.dom ; // 此属性 接受所得到的元素 // 如果匹配成功 则接受dom元素 arguments[0] = '#inp' if(idselector.test(arguments[0])){ this.dom = document.getElementById(arguments[0].substring(1)); } else { throw new Error(' arguments is error !'); } }; // 在Function类上扩展一个可以实现链式编程的方法 Function.prototype.method = function(methodName , fn){ this.prototype[methodName] = fn ; return this ; //链式编程的关键 } // 在_$的原型对象上 加一些公共的方法 _$.prototype = { constructor : _$ , addEvent:function(type,fn){ // 给你的得到的元素 注册事件 if(window.addEventListener){// FF this.dom.addEventListener(type , fn); } else if (window.attachEvent){// IE this.dom.attachEvent('on'+type , fn); } return this ; }, setStyle:function(prop , val){ this.dom.style[prop] = val ; return this ; } }; // window 上先注册一个全局变量 与外界产生关系 window.$ = _$ ; // 写一个准备的方法 _$.onReady = function(fn){ // 1 实例化出来_$对象 真正的注册到window上 window.$ = function(){ return new _$(arguments); }; // 2 执行传入进来的代码 fn(); // 3 实现链式编程 _$.method('addEvent',function(){ // nothing to do }).method('setStyle',function(){ // nothing to do }); }; })(window); // 程序的入口 window传入作用域中 $.onReady(function(){ var inp = $('#inp'); //alert(inp.dom.nodeName); //alert($('#inp')); inp.addEvent('click',function(){ alert('我被点击了!'); }).setStyle('backgroundColor' , 'red'); });
4. Verwenden Sie Rückruffunktionen, um Daten von Methoden abzurufen, die Kettenaufrufe unterstützen
Verkettete Aufrufe eignen sich sehr gut für Zuweisungsmethoden, aber für Wertabrufmethoden ist dies unpraktisch, da jede Methode dies zurückgibt.
Es gibt jedoch immer noch einen Workaround, und das ist die Rückruffunktion.
Wenn die Rückruffunktion nicht verwendet wird
//without callback window.API = window.API || function(){ var name = 'JChen'; this.setName = function(newName){ name = newName; return this; }; this.getName = function(){ return name; }; }; var o = new API(); console.log(o.getName()); console.log(o.setName('Haha').getName());
Bei Verwendung der Rückruffunktion
//with callback window.API2 = window.API2 || function(){ var name = 'JChen'; this.setName = function(newName){ name = newName; return this; }; this.getName = function(callback){ callback.call(this, name); return this; }; }; var o2 = new API2(); o2.getName(console.log).setName('Hehe').getName(console.log);
Bei der Verwendung von Rückruffunktionen ist callback.call(this, name) im Allgemeinen in Ordnung. In diesem Beispiel wird jedoch console.log verwendet, sodass ein Problem vorliegt. Der Grund dafür ist, dass dieser Wert der Konsole auf die Konsole und nicht auf winodw verweist.
Auch dieses Problem lässt sich leicht lösen. Wie folgt:
//with callback window.API2 = window.API2 || function(){ var name = 'JChen'; this.setName = function(newName){ name = newName; return this; }; this.getName = function(callback){ callback.call(this, name); return this; }; }; var o2 = new API2(); var log = function(para){ console.log(para); }; o2.getName(log).setName('Hehe').getName(log);
Diese Art des Kettenaufrufs vereinfacht das Schreiben von Code, macht den Code prägnanter und leichter lesbar und vermeidet außerdem die mehrfache Wiederverwendung einer Objektvariablen. Ich hoffe, dass jeder damit umgehen kann.