ES6 Proxies: отслеживание выполнения неопределенных функций

1

Скажем, у меня есть следующий объект с двумя функциями как свойствами:

const foo = {
  f1: () => {...},
  f2: () => {...},
}

Я хотел бы выполнить конкретное действие (например, вызвать пользовательскую ошибку), когда кто-то пытается выполнить функцию, которая не существует в объекте foo.

Я пробовал использовать get proxy, но это вызывает ошибку, даже если я не пытаюсь выполнить f3, например, в следующем коде:

if (foo.f3) {...}

Итак, как я могу написать свой прокси-сервер таким образом, что foo.f3 возвращает undefined как обычно, но foo.f3() действительно foo.f3() ошибку?

  • 1
    Вам не нужен прокси, чтобы сделать это. Нормальный объект уже делает все, что вы только что запросили.
  • 0
    @PatrickRoberts Если он не хочет выдать пользовательскую ошибку, верно?
Показать ещё 6 комментариев
Теги:
ecmascript-6

3 ответа

0
Лучший ответ

Здесь частичное решение, вдохновленное Unmiss.

const handler = {
  get: function(obj, prop) {
    if (prop in obj) {
      return obj[prop];
    } else {
      return () => {
        throw new Error('Foo.${prop} is undefined');
      }
    }
  }
};

Проблема с этим состоит в том, что в то время как он выполняет цель только метания ошибки, когда вы на самом деле пытаетесь выполнить Foo.f3(), так как Foo.f3 теперь равен этой анонимную функцию не возвращает undefined больше, а это означает, что ( насколько я могу сказать), if (Foo.f3) {...} всегда будет возвращать true.

Изменение: как указывает @paulpro:

Вы абсолютно не можете этого сделать. foo.f3 является либо неопределенным, либо некоторым вызываемым с пользовательской логикой; это не может быть и то, и другое.

Лучшее, что мы могли бы сделать, это ловушка f3 in foo операторах f3 in foo с использованием ловушки has, но это будет означать, if (f3 in foo) и if (foo.f3) теперь будут иметь разные результаты, которые выглядят как большой красный флаг.

0

Хорошо, поэтому мне понадобилось какое-то время, но теперь у меня есть решение.

Я не полностью понял ваш вопрос, который я переформулировал как вопрос в вопросе, чтобы я лучше понял проблему, поскольку это сложно.

В принципе, вы хотите знать, вызвана ли она или нет, поэтому функция, которая вам нужна в прокси-серверах get 'is is isCalling.

Решение не чиста в JS Fiddle, потому что оно беспорядочно существует, по крайней мере, для решения такого рода проблем.

В основном решение - это предложение: "вам нужно использовать ошибку, чтобы получить трассировку стека, а затем восстановить исходный код, который вызывает и искать правую скобку или нет". Чтобы определить, как его вызывать и возвращать все, что вам нужно затем).

[Обратите внимание, что это зависит от вашего кода и того, как вы его называете, чтобы вы приспособились по мере необходимости.]

Поскольку вам нужно найти местоположение в исходном коде, которое вызывается из него, лучше, если нет встроенного тега скрипта, как это имеет место в этом примере JSFiddle. Я использую externalHTML для получения источника, когда arguments.callee.caller.toString() лучше из реального JS файла. Вы также не найдете местоположение из stacktrace, которое искажено нечетным поведением здесь, поэтому с обычным JS файлом рекомендуется правильно выровнять код с использованием других решений. Если кто-то знает, как получить чистый источник, который каждый раз выравнивается с трассировкой ошибок с помощью блоков сценариев-тегов и т.д. Также обратите внимание, что приходящие, но не существующие, такие вещи, как Error.lineNumber.

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

Во всяком случае, пример, который я считаю, достигает того, чего вы хотите, но в принципе демонстрирует, что вам нужно делать лучше в данной реальной ситуации (без скрипта). Я почти уверен, что это не отличное решение в производстве, и я не тестировал время (скорость работы), но если это действительно было так важно для вашего дела (и никакое другое лучшее решение, которое я сомневаюсь), то это будет Работа.

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

Использование обширно, когда вы начинаете думать об этом больше, как в прошлом году, когда я впервые сделал что-то подобное. Примеры - это выполнение дополнительных функций. Проверка безопасности. Отладочное решение тайны-загадки в реальном времени, способ выполнения функции по-разному, не передавая больше аргументов, рекурсивные циклы убегающих (как долго это стек), чтобы назвать несколько.

https://jsfiddle.net/MasterJames/bhesz1p7/90/

let foo = new Proxy(
    {
    f1: function (val) {
      console.log('  F1 value:' + val);
      return 'called OKAY with', val;
    }
  },
  {
    isCalling: function() {
      let stk = new Error();
      let sFrms = this.stkFrms(stk.stack);
      console.log("stkFrms:", sFrms);

      //BETTER From real pure JS Source
      //let srcCod = arguments.callee.caller.toString()

      let srcCod = document.getElementsByTagName('html')[0].outerHTML.split("\n");
      let cItm = sFrms[(sFrms.length - 1)];
      if(cItm !== undefined) {
        let cRow = (parseInt(cItm[1]) - 3);
        let cCol = (parseInt(cItm[2]) + 1);
        let cLine = srcCod[cRow];
        let cCod = cLine.substr(cCol, 1);
        if(cCod === '(') return true;
      }
      return false;
    },
    stkFrms: function (stk) {
      let frmRegex1 = /^.*at.*\(.*\:([0-9]*)\:([0-9]*)\)$/;
      let frmRegex2 = new RegExp(frmRegex1.source, 'gm');
      let res = [], prc, mtch, frms = stk.match(frmRegex2);
      for(mtch of frms) {
        prc = frmRegex1.exec(mtch);
        res.push(prc);
      }
      return res;
    },
    get: function(obj, prop) {
      if (prop in obj) {
        console.log("Found:", prop);
        return obj[prop];
      }
      else {
        if(this.isCalling() === false) {
          console.log("Did NOT find:", prop);
          return undefined;
        }
        else {
          console.log("Did NOT find return custom throw function:", prop);
          return function() {throw new Error('Foo.${prop} is undefined');}
        }
      }
    }
  });

console.log("foo.f1:", foo.f1);
console.log("foo.f1('passed'):", foo.f1('passed'));

console.log("foo.f2:", foo.f2);
try {
    console.log("foo.f2('passed2'):", foo.f2('passed2'));
}
catch(err) {
    console.log("foo.f2('passed2') FAILED:", err);
}
console.log("'f2' in foo:", 'f2' in foo);

Хорошо, так что словесный пробег:

Вы хотите проверить, что foo.f2 не определено, поэтому оно возвращает это, потому что оно не вызывается.

Если вы вызываете его (f2), не проверяя при этом сначала и ошибки, а вы не хотите, чтобы попытаться поймать вашу пользовательскую ошибку на основе имени функции, вы хотите, чтобы она возвращала фактическую функцию, которая пользовательская ошибка.

Вы также хотите использовать "in", чтобы увидеть, что он не определен, что является таким же, как false (возможно, взломайте его дальше, чтобы отправить false вместо undefined с помощью чего-то вроде isCallingFromIn.

Я что-то пропустил? Разве это не то, что вы считали невозможным?

  • 0
    @ Саша не уверен, что вы видели это решение, я надеялся на обратную связь, так как не думаю, что для достижения вашей цели нужно больше, по крайней мере, теоретически.
0

Это то, о чем вы просите? https://jsfiddle.net/MasterJames/bhesz1p7/23/

[очевидно, вам нужно использовать F12 для своих инструментов для просмотра вывода консоли или изменения по желанию]

Только реальная разница заключается в том, чтобы вернуть неопределенные после метания. Это как если бы функция выполнялась без каких-либо действий, поскольку она не существует.

Я уверен, что существует другое решение, основанное на фактическом использовании, но мне нравится идея/вопрос. Сохраняет вещи более стабильными и т.д.

let foo = new Proxy(
    {
    f1: function (val) {
      console.log('  F1 value:' + val);
      return 'called OKAY with', val;
    }
  },
  {
    get: function(obj, prop) {
      console.log("obj:", obj, " prop:", prop);
      if (prop in obj) {
        console.log("Found:", prop);
        return obj[prop];
      }
      else {
        console.log("Did NOT find:", prop);
        throw new Error('Foo.${prop} is undefined not called returning undefined');
        return undefined;
      }
    }
  });

console.log("\nFoo Tester started");

console.log(' Does F1 exists', foo.f1 !== undefined);
console.log(' called F1 result:', foo.f1('passed') );

try {
    console.log(' Does F2 exists', foo.f2 !== undefined);
    console.log(' called F2 result:', foo.f2('passed') );
}
catch (err) {
    console.log(' Error calling F2:', err );
}

console.log("Foo Tester finished");

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

if (foo.f2 && foo.f2.constructor === Function && foo.f2()) console.log("okay!");

Опять вы вызываете сборку оболочки safeCall более похожим на это или что-то среднее? возможно вызывать foo 'customThrow', если он существует или что-то есть, так много возможностей с JS.

  • 0
    К сожалению, с этим шаблоном просто пишем `console.log ('Существует ли F2', foo.f2! == undefined);` уже выдает ошибку.
  • 0
    Я думаю, что я говорю об альтернативе в последней строке, чтобы не быть в паре с ней. Тем не менее я борюсь с вариантом использования или мотивацией. Это всего лишь взлом того, что вы опубликовали, и теперь я вижу, как вы хотите вернуть функциональную функцию, которая вызывает ошибку, но все еще видит неопределенную. Я предполагаю, что это просто двухэтапный процесс проверки и ошибок, как того требует сценарий использования.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню