Javascript: знать, какая функция объекта была вызвана - PullRequest
0 голосов
/ 26 февраля 2019

Я хотел бы знать, есть ли способ получить уведомление при вызове какой-либо объектной функции в javascript.

Например, я хотел бы сделать что-то вроде следующего:

Если у меня есть такой объект:

myObject = {
    functionOne: function(argumentOne) {
        // do some stuff
    },

    functionTwo: function() {
        // do some stuff
    }
}

И добавьте прослушиватель (или что-нибудь еще для отслеживания действий, выполняемых над этим объектом):

myObject.addEventListener('onFunctionCall', functionHasBeenCalled);

Когда я вызываю:

myObject.functionOne('hello');

Обработчик слушателя для запуска с информацией о вызываемой функции:

functionHasBeenCalled(calledFunctionData) {
    console.log(calledFunctionData.functionName + ' has been called');
    console.log('with argument: ' + calledFunctionData.functionArgument);
}

И вывод на консоль должен быть:

functionOne has been called
with argument: hello

Может быть, есть другойКстати, реализовать это не с помощью прослушивателя событий, но я понятия не имею.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Вы не можете сделать это, не вставая между объектом и тем, что его вызывает, или изменяя объект.Не существует никакого «прикосновения к этому взаимодействию», которое не затрагивает ни то, ни другое.

Вот каждый из них:

Как добраться между ними

Если вы можете получить междуобъект и вызывающая его вещь, вы можете создать для этого новый объект с функциями, дублирующими функции целевого объекта, который его вызывает.Вот простой пример:

const original = {
    value: 42,
    doSomething() {
        console.log(`original doSomething: this.value is ${this.value}`);
    },
    doSomethingElse() {
        console.log(`original doSomethingElse: this.value is ${this.value}`);
    }
};

const insertion = {};
for (const key of Object.keys(original)) {
    const func = original[key];
    if (typeof func === "function") {
        insertion[key] = function(...args) {
            console.log("insertion " + key + " [before]");
            const thisArg = this === insertion ? original : this;
            const result = Reflect.apply(func, thisArg, args);
            console.log("insertion " + key + " [after]");
            return result;
        };
    }
}

// Here's the code calling the object's methods;
// note we've gotten in the way by giving the code
// `insertion` rather than `original`:
insertion.doSomething();
insertion.doSomethingElse();

Вы также можете сделать это с помощью Proxy, хотя это более сложно и усложнение ничего не дает в этом случае.

Обратите внимание, что по очевидным причинам он будет перехватывать только вызовы, сделанные через insertion.Это означает, что если doSomething вызывает doSomethingElse, в приведенном выше случае вы перехватите вызов doSomething, но не вызов doSomethingElse.

Изменение объекта

Вы можетесделайте это, заменив методы объекта следующим образом:

const original = {
    value: 42,
    doSomething() {
        console.log(`original doSomething: this.value is ${this.value}`);
    },
    doSomethingElse() {
        console.log(`original doSomethingElse: this.value is ${this.value}`);
    }
};

for (const key of Object.keys(original)) {
    const func = original[key];
    if (typeof func === "function") {
        original[key] = function(...args) {
            console.log(key + " [before]");
            const result = Reflect.apply(func, this, args);
            console.log(key + " [after]");
            return result;
        };
    }
}

// Here's the code calling the object's methods
original.doSomething();
original.doSomethingElse();

Поскольку это изменяет сам объект, вы увидите все вызовы.

0 голосов
/ 26 февраля 2019

Одним из подходов может быть использование Proxy для создания «отслеживаемых» объектов, где вы перехватываете любые вызовы методов:

function trackMethodCalls(obj) {
  const handler = {
    // anytime we do obj.someMethod
    // we actually return the interceptedMethod instead
    get(target, propKey, receiver) {
      
      const method = target[propKey];

      // we only do something special if we're working with a function
      // on the object. If the property isn't a function we can just return
      // it as normal.
      if (typeof method !== 'function') {
        return method;
      }

      return function interceptedMethod(...args) {
        const result = method.apply(this, args);

        console.log(
          `${propKey}(${args.join(",")}) = ${JSON.stringify(result)}`
        );

        return result;
      };
    }
  };
  return new Proxy(obj, handler);
}

const obj = {
  val: 2,
  double(x) {
    return this.val * x;
  }
};

const trackedObj = trackMethodCalls(obj);

trackedObj.double(4);

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

function addTrackedMethods(obj) {
  for (const [methodName, method] of Object.entries(obj).filter(
    ([, method]) => typeof method === "function"
  )) {
    obj[methodName] = function interceptedMethod(...args) {
      const result = method.apply(this, args);

      console.log(
        `${methodName}(${args.join(",")}) = ${JSON.stringify(result)}`
      );

      return result;
    };
  }
}

const obj = {
  val: 2,
  double(x) {
    return this.val * x;
  }
};

addTrackedMethods(obj);

obj.double(4);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...