Как мне обернуть функцию в Javascript? - PullRequest
25 голосов
/ 28 ноября 2008

Я пишу глобальный «модуль» обработки ошибок для одного из моих приложений.

Одна из функций, которые я хочу иметь, - это возможность легко обернуть функцию блоком try{} catch{}, чтобы все вызовы этой функции автоматически имели код обработки ошибок, который вызовет мой метод глобальной регистрации. (Чтобы не загрязнять код везде блоками try / catch).

Это, однако, немного выходит за рамки моего понимания низкоуровневого функционирования JavaScript, методов .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, а затем вызывает мой глобальный обработчик ошибок.

Мой вопрос: это что-то сломает, когда функция, которую я обертываю, находится внутри объекта, и она использует оператор "this"? Я немного волнуюсь, так как я звоню .apply, передавая что-то там, я боюсь, что это может что-то сломать.

Ответы [ 5 ]

46 голосов
/ 29 ноября 2008

Лично вместо того, чтобы загрязнять встроенные объекты, я бы выбрал декораторскую технику:

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

Очевидным улучшением будет параметризация makeSafe (), чтобы вы могли указать, какую функцию вызывать в блоке catch.

7 голосов
/ 17 мая 2016

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);
2 голосов
/ 28 октября 2012

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 = {}

0 голосов
/ 05 мая 2019

Функция упаковки в старом добром стиле:

//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);
0 голосов
/ 29 ноября 2008

Что касается загрязнения пространств имен, я на самом деле собираюсь загрязнить их еще ... Поскольку все, что происходит в JS, инициируется каким-либо событием, я планирую вызвать свою магическую функцию-обертку из метода Prototype Event.observe (), поэтому мне не нужно вызывать ее везде.

Конечно, я вижу недостатки всего этого, но этот конкретный проект так или иначе сильно привязан к Prototype, и я хочу, чтобы этот код обработчика ошибок был как можно более глобальным, так что это не имеет большого значения.

Спасибо за ваш ответ!

...