كيف يمكنني لف وظيفة في JavaScript؟
-
11-07-2019 - |
سؤال
أنا أكتب خطأ عالمي معالجة "الوحدة النمطية" لأحد تطبيقاتي.
واحدة من الميزات التي أريد الحصول عليها هي أن أكون قادرًا على لف وظيفة مع أ try{} catch{}
كتلة ، بحيث يكون لجميع المكالمات إلى هذه الوظيفة تلقائيًا رمز معالجة الأخطاء تلقائيًا الذي سيتصل بأسلوب التسجيل العالمي الخاص بي. (لتجنب تلويث الكود في كل مكان باستخدام كتل Try/Catch).
هذا ، مع ذلك ، إلى ما هو أبعد قليلاً عن فهمي لأداء المستوى المنخفض لجافا سكريبت ، .call
و .apply
الطرق ، و this
الكلمة الرئيسية.
لقد كتبت هذا الرمز ، استنادًا إلى النموذج الأولي Function.wrap
طريقة:
Object.extend(Function.prototype, {
TryCatchWrap: function() {
var __method = this;
return function() {
try { __method.apply(this, arguments) } catch(ex) { ErrorHandler.Exception(ex); }
}
}
});
الذي يستخدم مثل هذا:
function DoSomething(a, b, c, d) {
document.write(a + b + c)
alert(1/e);
}
var fn2 = DoSomething.TryCatchWrap();
fn2(1, 2, 3, 4);
هذا الرمز يعمل تماما. يطبع 6 ، ثم يستدعي معالج الخطأ العالمي.
سؤالي هو: هل يكسر هذا شيئًا عندما تكون الوظيفة التي ألغفها داخل كائن ، وتستخدم المشغل "هذا"؟ أنا قلق بعض الشيء منذ أن أتصل. ابتكر شيئًا ما هناك ، أخشى أن هذا قد يكسر شيئًا ما.
المحلول
شخصياً بدلاً من تلويث الأشياء المبنية ، أود أن أذهب مع تقنية الديكور:
var makeSafe = function(fn){
return function(){
try{
return fn.apply(this, arguments);
}catch(ex){
ErrorHandler.Exception(ex);
}
};
};
يمكنك استخدامه هكذا:
function fnOriginal(a){
console.log(1/a);
};
var fn2 = makeSafe(fnOriginal);
fn2(1);
fn2(0);
fn2("abracadabra!");
var obj = {
method1: function(x){ /* do something */ },
method2: function(x){ /* do something */ }
};
obj.safeMethod1 = makeSafe(obj.method1);
obj.method1(42); // the original method
obj.safeMethod1(42); // the "safe" method
// let's override a method completely
obj.method2 = makeSafe(obj.method2);
ولكن إذا شعرت برغبة في تعديل النماذج الأولية ، فيمكنك كتابتها على هذا النحو:
Function.prototype.TryCatchWrap = function(){
var fn = this; // because we call it on the function itself
// let's copy the rest from makeSafe()
return function(){
try{
return fn.apply(this, arguments);
}catch(ex){
ErrorHandler.Exception(ex);
}
};
};
سيكون التحسن الواضح هو تحديد معلمة Makeafe () حتى تتمكن من تحديد وظيفة الاتصال في كتلة الصيد.
نصائح أخرى
2017 الجواب: فقط استخدم ES6. بالنظر إلى الوظيفة التجريبية التالية:
var doThing = function(){
console.log(...arguments)
}
يمكنك جعل وظيفة الغلاف الخاصة بك دون الحاجة إلى مكتبات خارجية:
var wrap = function(someFunction){
var wrappedFunction = function(){
var args = [...arguments].splice(0)
console.log(`You're about to run a function with these arguments: \n ${args}`)
return someFunction(args)
}
return wrappedFunction
}
في الاستخدام:
doThing = wrap(doThing)
doThing('one', {two:'two'}, 3)
إجابة 2016: استخدم ال wrap
وحدة:
في المثال أدناه أنا ألغى process.exit()
, ، لكن هذا يعمل بسعادة مع أي وظيفة أخرى (بما في ذلك المتصفح JS أيضًا).
var wrap = require('lodash.wrap');
var log = console.log.bind(console)
var RESTART_FLUSH_DELAY = 3 * 1000
process.exit = wrap(process.exit, function(originalFunction) {
log('Waiting', RESTART_FLUSH_DELAY, 'for buffers to flush before restarting')
setTimeout(originalFunction, RESTART_FLUSH_DELAY)
});
process.exit(1);
Object.Extend (function.prototype ، {Object.Extend في وحدة التحكم في Google Chrome ، تعطيني "غير محدد" جيدًا ، إليك بعض مثال العمل:
Boolean.prototype.XOR =
// ^- Note that it's a captial 'B' and so
// you'll work on the Class and not the >b<oolean object
function( bool2 ) {
var bool1 = this.valueOf();
// 'this' refers to the actual object - and not to 'XOR'
return (bool1 == true && bool2 == false)
|| (bool1 == false && bool2 == true);
}
alert ( "true.XOR( false ) => " true.XOR( false ) );
لذا بدلاً من Object.Extend (function.prototype ، {...}) افعلها مثل: function.prototype.extend = {}
وظيفة التفاف بطريقة قديمة جيدة:
//Our function
function myFunction() {
//For example we do this:
document.getElementById('demo').innerHTML = Date();
return;
}
//Our wrapper - middleware
function wrapper(fn) {
try {
return function(){
console.info('We add something else', Date());
return fn();
}
}
catch (error) {
console.info('The error: ', error);
}
}
//We use wrapper - middleware
myFunction = wrapper(myFunction);
نفس الشيء في نمط ES6:
//Our function
let myFunction = () => {
//For example we do this:
document.getElementById('demo').innerHTML = Date();
return;
}
//Our wrapper - middleware
const wrapper = func => {
try {
return () => {
console.info('We add something else', Date());
return func();
}
}
catch (error) {
console.info('The error: ', error);
}
}
//We use wrapper - middleware
myFunction = wrapper(myFunction);
بقدر ما أروي مساحات الأسماء ، سأقوم بالفعل بتلويثها أكثر ... نظرًا لأن كل ما يحدث في JS يبدأ بحدث من نوع ما ، أخطط للاتصال بوظيفة الغلاف السحري من داخل حدث النموذج الأولي طريقة. OBServe () ، لذلك لست بحاجة إلى تسميتها في كل مكان.
أرى الجوانب السلبية لكل هذا ، بالطبع ، لكن هذا المشروع بالذات مرتبط بشدة بالنموذج الأولي على أي حال ، وأريد أن يكون رمز معالج الأخطاء هذا عالميًا قدر الإمكان ، لذلك ليس مشكلة كبيرة.
شكرا لإجابتك!