Вопрос:

Создать вспомогательную функцию для запуска функции в изолированной области

javascript node.js

132 просмотра

3 ответа

21420 Репутация автора

Этот код работает:

  it.cb(h => {
    console.log(h);
    h.ctn();
  });

  it.cb(new Function(
    'h', [
      'console.log(h)',
      'h.ctn()'
    ]
    .join(';')
  ));

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

 it.cb(isolated(h => {
    console.log(h);
    h.ctn();
 }));

где изолированные - это вспомогательная функция, которая выглядит примерно так:

const isolated = function(fn){
   const str = fn.toString();
   const paramNames = getParamNames(str);
   return new Function(...paramNames.concat(str));
};

самая большая проблема заключается в том, что Function.prototype.toString()дает вам всю функцию. Кто-нибудь знает хороший способ просто получить тело функции из строкового представления функции?

Обновление: PRoberts спрашивал, какова цель этого, цель просто:

 const foo = 3;

 it.cb(isolated(h => {
    console.log(foo);  // this will throw "ReferenceError: foo is not defined"
    h.ctn();
 }));
Автор: Alexander Mills Источник Размещён: 03.01.2018 05:23

Ответы (3)


1 плюс

6903 Репутация автора

Я бы просто использовал indexOf('{')и lastIndexOf('}').

const yourFunction = h => {
    console.log(h);
    h.ctn();
};

const fnText = yourFunction.toString();
const body = fnText.substring(fnText.indexOf('{') + 1, fnText.lastIndexOf('}'));

console.log(body);

Зная, что это не будет охватывать функции стрелок без тела:

const fn = k => k + 1
Автор: klugjo Размещён: 03.01.2018 05:39

0 плюса

21420 Репутация автора

Хорошо, это работает, это не было слишком сложно. Мы просто предполагаем, что первый и последний парены являются контуром тела функции.

const isolated = function(fn){
  const str = fn.toString();
  const first = str.indexOf('{') + 1;
  const last = str.lastIndexOf('}');
  const body = str.substr(first, last-first);
  const paramNames = ['h'];
  return new Function(...paramNames.concat(body));
};

Выше мы предполагаем, что единственный аргумент называется «h», но вам нужно будет найти парсер аргументов функции. Я использовал require('function-arguments')в прошлом.

Автор: Alexander Mills Размещён: 03.01.2018 05:58

2 плюса

23235 Репутация автора

Решение

Я написал версию, isolated()которая обрабатывает любое нередактированное bindвыражение пользовательской функции и генерирует пользовательские ошибки для доступа по областям:

function isolated (fn) {
  return new Function(`
    with (new Proxy({}, {
      has () { return true; },
      get (target, property) {
        if (typeof property !== 'string') return target[property];
        throw new ReferenceError(property + ' accessed from isolated scope');
      },
      set (target, property) {
        throw new ReferenceError(property + ' accessed from isolated scope');
      }
    })) return ${Function.prototype.toString.call(fn)}
  `).call(new Proxy(function () {}, new Proxy({}, {
    get() { throw new ReferenceError('this accessed from isolated scope'); }
  })));
}

// test functions
[
  () => arguments, // fail
  () => this, // pass, no way to intercept this
  () => this.foo, // fail
  () => this.foo = 'bar', // fail
  () => this(), // fail
  () => new this, // fail
  h => h, // pass
  h => i, // fail
  (a, b) => b > a ? b : a, // pass
].forEach(fn => {
  const isolate = isolated(fn);
  console.log(isolate.toString());

  try {
    isolate();
    console.log('passed');
  } catch (error) {
    console.log(`${error.name}: ${error.message}`);
  }
})

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

withУтверждение является относительно упрощенным способом ловли любых контекстных ссылок в принудительно изолированных функциях и бросать ReferenceError. Это делается путем вставки Proxyпромежуточного объекта в область действия с ловушкой get, которая перехватывает имя доступной переменной области.

То, Proxyчто передается в качестве контекста функции, было единственной частью, которая была немного сложна для реализации, а также неполной. Это было необходимо, поскольку Proxyпредоставляемая в качестве области действия withоператора не перехватывает доступ к thisключевому слову, поэтому контекст также должен быть явно обернут, чтобы перехватывать и использовать любое косвенное использование thisвнутри изолированной функции стрелки.

Автор: Patrick Roberts Размещён: 03.01.2018 07:41
Вопросы из категории :
32x32