En JavaScript, il y a trop de codes de modèles de chaîne, comme suit :
if_else :
if(...){ //TODO }else if(...){ //TODO }else{ //TODO }
interrupteur :
switch(name){ case ...:{ //TODO break; } case ...:{ //TODO break; } default:{ //TODO } }
Question : Comme le code de chaîne ci-dessus, si on veut l'aplatir et l'enchaîner Tissu de laine ? Comme suit :
//fn1,f2,f3为处理函数 _if(fn1)._elseIf(fn2)._else(fn3);
Essayons de le mettre en œuvre ensemble.
Supposons maintenant que nous ayons le code de chaîne suivant :
if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else if(name === 'Dorie'){ console.log('yes, I am Dorie'); }else{ console.log('sorry, over for ending!'); }
D'accord, maintenant nous allons l'aplatir étape par étape" Plat".
En fait, en regardant le code ci-dessus, il n'est pas difficile de constater que le format if...else est en fait une liste à lien unique dans la structure de données. Ensuite, utilisez initialement JavaScript pour implémenter une liste à lien unique. liste, comme suit :
var thens = []; thens.resolve = function(name){ for(var i = 0, len = this.length; i < len;i++){ if(this[i](name) !== 'next'){ break; } } } thens.push(f1, f2, f3);
Parmi eux, f1, f2 et f3 sont des fonctions de jugement, et nous supposons que si f1, f2 et f3 renvoient « suivant », nous continuerons à chercher vers le bas, sinon, nous arrêterons de chercher vers le bas. Comme suit :
function f1(name){ if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else{ return 'next'; } } function f2(name){ if(name === 'Dorie'){ console.log('yes, I am Dorie'); }else{ return 'next'; } } function f3(){ console.log('sorry, over for ending!'); }
D'accord, c'est le modèle de la liste chaînée.
Cependant, notre objectif ultime est d'atteindre ce qui suit ?
//fn1,f2,f3为处理函数 _if(fn1)._elseIf(fn2)._else(fn3);
Vous pourriez dire, ne serait-il pas bien de changer le code ci-dessus par le suivant ? ! !
thens.push(f1).push(f2).push(f3).resolve();
Mais, La méthode push de JavaScript renvoie la nouvelle longueur du tableau, pas l'objet tableau.
Donc, alors nous ne pouvons écrire qu'une nouvelle méthode add, qui a le même effet que push, mais renvoie un objet tableau. Comme suit :
thens.add = function(f){ if(typeof f === 'function'){ this.push(f); return this; } }
Le code de test est le suivant :
var thens = []; thens.add = function(f){ if(typeof f === 'function'){ this.push(f); return this; } } thens.resolve = function(name){ for(var i = 0, len = this.length; i < len;i++){ if(this[i](name) !== 'next'){ break; } } } thens.add(f1).add(f2).add(f3).resolve();
Cependant, cela présente un inconvénient. Nous lions les méthodes d'ajout et de résolution aux méthodes d'ajout et de résolution. variable globale thens Vous ne pouvez pas simplement copier et coller la méthode à chaque fois que vous créez un tableau, donc le code refactorisé est le suivant :
function Slink(){ this.thens = []; this.thens.add = function(f){ if(typeof f === 'function'){ this.push(f); return this; } } this.thens.resolve = function(name){ for(var i = 0, len = this.length; i < len;i++){ if(this[i](name) !== 'next'){ break; } } } }
Évidemment, les méthodes publiques comme add et solve seront utilisées à chaque fois c'est instancié. , il n'est pas scientifique de tous les créer, alors utilisez le prototype pour continuer à déformer sur la base d'origine, comme suit :
function Slink(){ this.thens = []; } Slink.prototype = { add: function(f){ if(typeof f === 'function'){ this.thens.push(f); return this; } }, resolve: function(name){ for(var i = 0, len = this.thens.length; i < len; i++){ if(this.thens[i](name) !== 'next'){ break; } } } }
Le code de test est le suivant :
var thens = new Slink(); thens.add(f1).add(f2).add(f3); thens.resolve();
Oui, mais dans ce cas, nous devons créer manuellement un nouveau Slink à chaque fois, ce qui est un peu gênant. Par conséquent, nous encapsulons le nouveau processus Slink dans une fonction, tout comme jQuery, comme. suit :
function $go(f){ return new Slink(f); } function Slink(f){ this.thens = []; this.thens.push(f); } Slink.prototype = { add: function(f){ if(typeof f === 'function'){ this.thens.push(f); return this; } }, resolve: function(name){ for(var i = 0, len = this.thens.length; i < len; i++){ if(this.thens[i](name) !== 'next'){ break; } } } }
Le code du test est le suivant :
$go(f1).add(f2).add(f3).resolve();
D'accord, vous avez terminé, vient maintenant le problème du sucre grammatical. Le code est. organisé comme suit :
function _if(f){ return new Slink(f); } function Slink(f){ this.thens = []; this.thens.push(f); } Slink.prototype = { _elseIf: function(f){ if(typeof f === 'function'){ this.thens.push(f); return this; } }, _else: function(f){ return this._elseIf(f); }, resolve: function(name){ for(var i = 0, len = this.thens.length; i < len; i++){ if(this.thens[i](name) !== 'next'){ break; } } return this; } }
Le code du test est le suivant :
_if(f1)._elseIf(f2)._else(f3).resolve();
Bien sûr, en plus d'utiliser des tableaux, vous pouvez également utiliser fermetures pour obtenir un effet d'aplatissement de la chaîne, comme suit :
var func = Function.prototype; func._else = func._elseIf = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); if(res==="next"){ //值为Boolean return fn.apply(this,arguments); } return res; } }
Le code de test est le suivant :
function f1(name){ if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else{ return 'next'; } } function f2(name){ if(name === 'Dorie'){ console.log('yes, I am Dorie'); }else{ return 'next'; } } function f3(){ console.log('sorry, over for ending!'); } f1._elseIf(f2)._else(f3)('Dorie');
Ce dont nous avons discuté ci-dessus sont tous des processus synchrones. Si, les appels en chaîne Y a-t-il une situation asynchrone dans la fonction ?
Qu'est-ce que cela signifie ? Comme suit :
function f1(name){ setTimeout(function(){ if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else{ return 'next'; } }, 2000); } function f2(name){ if(name === 'Dorie'){ console.log('yes, I am Dorie'); }else{ return 'next'; } } function f3(){ console.log('sorry, over for ending!'); }
Nous avons rendu f1 asynchrone en utilisant setTimeout. Selon la logique du code ci-dessus, nous devrions attendre que f1 soit complètement exécuté (y compris l'exécution de setTimeout) avant de juger s'il faut exécuter f2, mais c'est vraiment le cas ?
Le code de test est le suivant :
_if(f1)._elseIf(f2)._else(f3).resolve();
Le résultat de l'exécution du code est que rien n'est sorti.
Pourquoi ?
Parce que JavaScript est monothread. Voir (ici) pour plus de détails
Alors comment le résoudre ?
Puisqu'il y a du code asynchrone et que la chaîne suivante doit être traitée après le code asynchrone, alors nous attendons que le code asynchrone soit terminé avant d'exécuter la chaîne suivante, comme suit :
function f1(name){ setTimeout(function(){ if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else{ //处理后续链 this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置 } }.bind(this), 2000); }
Eh bien, puisque dans la fonction, nous avons utilisé ceci, qui représente l'objet Slink, et modifié la méthode de résolution, nous devons affiner le constructeur Slink et la chaîne de prototypes, comme suit :
function Slink(f){ this.thens = []; this.thens.push(f.bind(this)); } Slink.prototype = { _elseIf: function(f){ if(typeof f === 'function'){ this.thens.push(f.bind(this)); return this; } }, _else: function(f){ return this._elseIf(f.bind(this)); }, resolve: function(name, flag){ for(var i = flag, len = this.thens.length; i < len; i++){ if(this.thens[i](name) !== 'next'){ break; } } return this; } }
Le test le code est le suivant :
function f1(name){ setTimeout(function(){ if(name === 'Monkey'){ console.log('yes, I am Monkey'); }else{ //处理后续链 this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置 } }.bind(this), 2000); } function f2(name){ if(name === 'Dorie'){ console.log('yes, I am Dorie'); }else{ return 'next'; } } function f3(){ console.log('sorry, over for ending!'); } _if(f1)._elseIf(f2)._else(f3).resolve('',0);
Haha, si tu connais Promise, tu penses que c'est si similaire ?
Oui, le but est le même, atteindre l'objectif d'aplatir le code asynchrone, mais le code ici est beaucoup plus simple que Promise. Pour plus de détails sur Promise, voir (ici).
Ce qui précède est l'explication détaillée de la sérialisation de la structure de la chaîne JavaScript. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !