1. Qu'est-ce que l'appel en chaîne
C'est facile à comprendre, par exemple :
$(this).setStyle('color', 'red').show();
La différence entre les appels de fonction généraux et les appels en chaîne : après avoir appelé une méthode, return this renvoie l'objet de la méthode appelante actuelle.
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. Décomposer les appels en chaîne
L'appel en chaîne est en fait composé de deux parties :
1). Objet d'opération (c'est-à-dire l'élément DOM manipulé, tel que $(this) dans l'exemple ci-dessus)
2). Méthode de fonctionnement (en particulier ce qu'il faut faire, comme setStyle et show dans l'exemple ci-dessus)
Comment implémenter des objets d'opération et des méthodes d'opération
Créez une fonction $ générique :
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; }
Cependant, si vous transformez cette fonction en constructeur, enregistrez ces éléments sous forme de tableau dans un attribut d'instance et laissez toutes les méthodes définies dans l'objet pointé par l'attribut prototype de la fonction constructeur revenir pour appeler la méthode Une référence à cette instance, il a alors la capacité d'enchaîner les appels. (Cela dit, retournez simplement ceci ; à la fin de chaque méthode),
Je dois d'abord changer cette $fonction en une méthode d'usine, qui est responsable de la création d'objets prenant en charge les appels en chaîne. Cette fonction doit pouvoir accepter des arguments sous la forme d'un tableau d'éléments afin que nous puissions utiliser la même interface publique qu'auparavant. De cette façon, il a la capacité de passer des appels en chaîne.
La transformation est la suivante :
(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); } })();
Renvoyer ceci à la fin, qui transmet l'objet de la méthode appelante à la méthode suivante dans la chaîne appelante.
3. Simuler la programmation en chaîne sous-jacente à 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. Utilisez les fonctions de rappel pour obtenir des données à partir de méthodes prenant en charge les appels en chaîne
Les appels chaînés conviennent très bien aux méthodes d'attribution, mais pour les méthodes de lecture de valeur, cela n'est pas pratique car chaque méthode renvoie ceci.
Cependant, il existe encore une solution de contournement, à savoir la fonction de rappel.
Lorsque la fonction de rappel n'est pas utilisée
//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());
Lors de l'utilisation de la fonction de rappel
//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);
Lors de l'utilisation des fonctions de rappel, callback.call(this, name) convient généralement. Cependant, dans cet exemple, console.log est utilisé, il y a donc un problème. La raison en est que celui de la console pointe vers la console plutôt que vers Winodw.
Ce problème est également facile à résoudre. Comme suit :
//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);
Ce style d'appel en chaîne permet de simplifier l'écriture du code, de le rendre plus concis et plus facile à lire, et évite également de réutiliser une variable objet plusieurs fois. J'espère que tout le monde pourra la maîtriser.