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

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

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

function test(){
  useState(123)
}

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


Ответы (2 шт):

Автор решения: DiD

Можно, например, распарсив (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('Неправильная функция');
}

→ Ссылка
Автор решения: vsemozhebuty

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

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 какой-то явный способ передавать информацию о контексте вызова.

→ Ссылка