JavaScriptのAOP構文糖

多くの場合、アプリケーションの実行時にデータを収集するコードにいくつかの追加ロジックを追加すると便利です。たとえば、呼び出し回数のカウントやエラー処理などです。 しかし、既存のコンパクトに記述されたコードを台無しにしたくはありません(もちろん、あなたがそのような幸せを持っているのでなければ)。 AOP技術の形式のソリューションは長い間存在しており、広く適用されます。 .NETおよびJavaプラットフォームでは、多くのAOPフレームワークは、メソッドおよびクラスに属性を適用することに焦点を当てています。 コードはほとんど変更されていませんが、自由に機能を拡張するためのかなり強力なメカニズムです。



JavaScriptにはこのようなフレームワークはそれほど多くありません。関数を展開するときに見つけたものは、通常のイベントサブスクリプションのようでした。 一般的に、構文はあまり好きではありませんでした。シンプルで、.NETとJavaの「重要事項」に近いものを望んでいました。



典型的なタスクを設定します。 ロジックを追加する必要があります:

•機能を実行する前

•機能を実行した後

•例外の場合



.NET属性は私に近いですし、そのような構文の実装はそれ自体を示唆しているので、C#の小さな例から進みます。

[AttributeName(Params)] [AttributeName(Params), AttributeName(Params)] public void Foo() { … }
      
      





JavaScriptで有効になるようにコードを少し変更します

 AOP( [AttributeName(Params)], [AttributeName(Params) , AttributeName(Params)], function Foo() { … } );
      
      





原則として、複雑なものは何も記述していません。すべてが非常に実行可能です。 AOP機能を実装し、ロジックの拡張を有能に整理することが残っています。

 function AOP() { var advices = []; //    for (var i = 0; i < arguments.length; i++) { var arg = arguments[i]; //     ,    , //      if (Object.prototype.toString.call( arg ) === "[object Array]") { for (var j = 0, advice = arg[j]; j < arg.length; j++) { advices.push(advice); } } else { if (typeof arg === "function") { name = getFunctionName(arg); //    // ..    for (var j = 0; j < advices.length; j++) { var advice = advices[j]; arg = advice.apply(arg); } //      , //   ,    //   .    .    //  FunctionName window[name] = arg; } //     advices.length = 0; } } } function getFunctionName(fn) { var source = fn.toString(); var head = source.split("(")[0]; var fnName = head.split(" ")[1]; return fnName; }
      
      







機能の拡張を整理します

 function Advice(implementation) { this.implementation = implementation; } // fn -      Advice.prototype.apply = function(fn) { if (typeof fn !== "function") { throw "You can apply an advice only to a function"; } //    return this.implementation(fn); }
      
      







そして、必要な属性を追加します

 function OnBeforeAdvice(onBeforeHandler) { return new Advice(function(fn) { return function() { onBeforeHandler(fn, arguments); return fn.apply(this, arguments); } }); } function OnAfterAdvice(onAfterHandler) { return new Advice(function(fn) { return function() { var result = fn.apply(this, arguments); onAfterHandler(fn, arguments, result); return result; } }); } function OnErrorAdvice(onErrorHandler) { return new Advice(function(fn) { return function() { try { return fn.apply(this, arguments); } catch (e) { onErrorHandler(fn, arguments, e); } } }); } //        //  "Pet.prototype.getName" function FunctionName(fnName) { return new Advice(function(fn) { var root = window, objs = fnName.split("."), i, oName; for (i = 0; i < objs.length - 1; i++) { oName = objs[i]; if (!root.oName) { root[oName] = {}; } root = root[oName]; } oName = objs[i]; root[oName] = fn; return fn; }); }
      
      







すべての準備が整いました。メソッドを作成して拡張できます

  <script type="text/javascript"> AOP( [OnBeforeAdvice(onBeforeHandler)], [OnAfterAdvice(onAfterHandler)], function sqr(x) { return x * x; }, [OnAfterAdvice(onBeforeHandler)], [FunctionName("AliasA")], function A() { }, [OnErrorAdvice(onErrorHandler)], function throwsException() { throw "Exception"; } ); function onBeforeHandler(fn, args) { console.log("I know that somebody calls " + fn.toString().split("(")[0]); } function onAfterHandler(fn, args, result) { console.log("Returns " + result + " from " + args[0]); } function onErrorHandler(fn, args, e) { console.log(e + " was thrown"); } onload = function () { sqr(10); A(); AliasA(); throwsException(); } </script>
      
      







そして今、あなたは「優雅に」機能に従うことができ、それらを1行でマークします。 AOP()関数を改良し、オブジェクトにも属性を適用できますが、これはわずかに異なるタスクになります。



ウィキペディアの記事には、JavaScriptのAOP実装へのリンクがあり、比較できます。 そして別のアスペクトJSプロジェクト



PS:evalについて公正な指摘をしました。 彼は、この記事で冗長と見なしたコードを返しました。



All Articles