Как узнать кто вызвал функцию

160
22 мая 2022, 13:20

Хочу узнать из функции которую вызвали, кто именно её вызвал, и чтобы можно было обращаться к этой функции и к её prototype.

let useState = val => {
 // как узнать здесь кто вызвал функцию?
}
function test(){
  useState(123)
}

такие методы как Function.caller не подходят, ибо очень старые. Как можно по новому узнать кто вызвал функцию.

Answer 1

Насколько я понимаю, надёжного способа нет.

Function.caller не рекомендовано к использованию.

Можно попробовать распарсить стек, но это тоже будет хрупкий костыль на костыле:

let useState = val => {
  const callerName = new Error().stack.split('\n')[2].split(/\s+/)[2];
  console.log(eval(callerName).prototype);
}
function test() {
  useState(123);
}
test.prototype.foo = 42;
test(); // { foo: 42 }

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

Answer 2

Можно, например, распарсив (new Error).stack.

function getStack(сколькоПропустить = 1){
  const регуляркаАнонима = /^at (\S+) \(<anonymous>\)$/,
        регуляркаФункции = /^at (\S+) \(([^:]+):([^:]+):(\d+):(\d+)\)$/,
        регуляркаФайла = /^at ([^:]+):([^:]+):(\d+):(\d+)$/;
  return (new Error).stack
    .split(/\n/g)
    .filter((строка,номер) => номер > сколькоПропустить)
    .map(строка => строка.trim())
    .map(строка => регуляркаАнонима.exec(строка)|| 
                   регуляркаФункции.exec(строка)||
                   регуляркаФайла.exec(строка))
    .map(результат => 
      результат.length < 3 ? 
        { функция: результат[1] } : 
      результат.length > 5 ? 
        { функция: результат[1], 
          файл:    `${результат[2]}:${результат[3]}`,
          строка:  результат[4],
          символ:  результат[5]} :
        { файл:    `${результат[1]}:${результат[2]}`,
          строка:  результат[3],
          символ:  результат[4]}
    );
}
const prototypeflag = Symbol('true');
iAmCallingThatFunction.prototype = {
  [prototypeflag]: true
};
function whoCallsMe(){
  const стэк = getStack(2);
  console.log('стэк', стэк);
  const функция = findFunctionWithNeededPrototype(стэк);
  console.log('функция', функция);
  const прототип = функция.prototype;
  console.log('нужный прототип?',прототип[prototypeflag]);
}
function iAmCallingThatFunction(){
  whoCallsMe();
}
iAmCallingThatFunction();
var Массив = class extends Array {
  get [prototypeflag](){ return !0; }
  methodThatCallsFunction(){
    this.forEach(function() {
      whoCallsMe();
    });
    this.forEach(whoCallsMe);
  }
}
const массив = new Массив(1).fill(0);
массив.methodThatCallsFunction();
function findFunctionWithNeededPrototype(стэк){
  for(let индекс = 0; индекс < стэк.length; индекс++){
    let имя = стэк[индекс].функция;
    if(!имя) continue;
    if(/\./.test(имя))
      имя = имя.replace(/\..*$/,'');
    if(имя && 'function' == typeof window[имя] &&
        window[имя].prototype[prototypeflag])
      return window[имя];
  }
  throw new Error('Неправильная функция');
}

READ ALSO
Как берутся проекты для разработки

Как берутся проекты для разработки

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

171
Валидация формы JavaScript

Валидация формы JavaScript

Хотел бы получить ваше мнение по моему коду

160
Как заанимировать эквалайзер

Как заанимировать эквалайзер

Есть задача заанимировать такой эквалайзер https://prntsc/vki1ks

158
Вырезать текст после определенного слова

Вырезать текст после определенного слова

Помогите пожалуйста с регулярными выражениямиЕсть строка

191