Jak zawinąć funkcję w Javascript?

Piszę globalny moduł obsługi błędów dla jednej z moich aplikacji.

Jedną z funkcji, którą chcę mieć, jest możliwość łatwego zawijania funkcji blokiem Try {} Catch {}, aby wszystkie wywołania tej funkcji automatycznie miały kod obsługi błędów, który wywoła moją globalną metodę logowania. (Aby uniknąć zanieczyszczania kodu wszędzie blokami try/catch).

Jest to jednak nieco poza moim zrozumieniem niskopoziomowego funkcjonowania Javascript, the. call i .zastosuj metody i słowo kluczowe "this".

Napisałem ten kod, bazując na funkcji Prototype ' a.metoda zawijania:

Object.extend(Function.prototype, {
  TryCatchWrap: function() {
    var __method = this;
    return function() {
            try { __method.apply(this, arguments) } catch(ex) { ErrorHandler.Exception(ex); }
    }
  }
});

Który jest używany tak:

    function DoSomething(a, b, c, d) {
        document.write(a + b + c)
        alert(1/e);
    }

    var fn2 = DoSomething.TryCatchWrap();
    fn2(1, 2, 3, 4);
Ten kod działa idealnie. Wyświetla 6, a następnie wywołuje mój globalny program obsługi błędów. Moje pytanie brzmi:.. Czy to coś złamie, gdy funkcja, którą zawijam, znajduje się wewnątrz obiektu i używa operatora "this"? Trochę się martwię, odkąd dzwonię .aplikuj, przechodząc coś tam, obawiam się, że to może rozbij coś.
Author: Daniel Magliola, 2008-11-28

4 answers

Osobiście zamiast zanieczyszczać budowane obiekty wybrałbym technikę dekoratorską:

var makeSafe = function(fn){
  return function(){
    try{
      return fn.apply(this, arguments);
    }catch(ex){
      ErrorHandler.Exception(ex);
    }
  };
};

Możesz go używać w ten sposób:

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);

Ale jeśli masz ochotę zmodyfikować prototypy, możesz napisać tak:

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);
    }
  };
};

Oczywistą poprawą będzie parametryzacja makeSafe (), dzięki czemu będziesz mógł określić, jaką funkcję wywołać w bloku catch.

 43
Author: Eugene Lazutkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-28 21:13:26

2017 odpowiedź : wystarczy użyć ES6. Biorąc pod uwagę następującą funkcję demo:

var doThing = function(){
  console.log(...arguments)
}

Możesz stworzyć własną funkcję wrappera bez potrzeby korzystania z zewnętrznych bibliotek:

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
}

W użyciu:

doThing = wrap(doThing)

doThing('one', {two:'two'}, 3)

2016 odpowiedź : użyj modułu wrap:

W poniższym przykładzie zawijam process.exit(), ale działa to szczęśliwie z każdą inną funkcją(w tym również js przeglądarki).

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);
 7
Author: mikemaccana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-17 19:03:21

Obiekt.extend (Function.prototype, { Obiekt.extend w konsoli Google Chrome daje mi 'undefined' Oto przykład pracy:

    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 ) );

Więc zamiast Obiekt.extend (Function.prototype, {...}) Zrób to jak: Funkcja.prototyp.extend = {}

 2
Author: DigitalDemenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-28 15:25:17

Jeśli chodzi o zanieczyszczanie przestrzeni nazw, zamierzam je jeszcze bardziej zanieczyścić... Ponieważ wszystko, co dzieje się w JS, jest inicjowane przez jakieś wydarzenie, planuję wywołać moją magiczną funkcję wrappera z poziomu zdarzenia prototypowego.metoda observe (), więc nie muszę jej wszędzie wywoływać.

Widzę minusy tego wszystkiego, oczywiście, ale ten konkretny projekt jest mocno związany z prototypem i chcę, aby ten kod obsługi błędów był tak globalny, jak możliwe, więc to nic wielkiego.

Dzięki za odpowiedź!

 -1
Author: Daniel Magliola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-28 21:26:09