Вывести имя полученной функции
Есть функция ( не работает ):
function showName(f){ var fName=f.name; alert(fName); // 'Rain' } function Rain(){ showName(this); // отправим себя } Rain() Дополнение Мне кажется туповато писать имя функции и обращаться к нему: https://developer.mozilla.org/ru/doc.../Function/name Понимаю, если имя функции не известно и мы хотим узнать его, как в моем вопросе, а тут получается типа: showName.name // showName или Тимур.имя // Тимур Мда... А Function.caller не стандартно. Эх... |
Teamur, ты никогда не узнаешь имя функции, передавая ее как параметр в другую функцию... :no:
|
ksa,
А если так: function showName(f){ alert(f+''); } Возможно так получится, но не могу проверить, с планшета |
ksa,
слушай, я в регулярках вообще не шарю, как мне получить содержимое первой скобки, дс как получить параметры из строки-функции? Смотрел варианты в сети, но там какие-то длинющие регулярки для простого получения содержимого между скобками. |
Цитата:
|
Цитата:
|
function getFunctionName(func) { if(typeof func === 'function') { const funcNameArr = /^\s*function\s*([^\(]*).*/i.exec(func.toString()); if(funcNameArr && funcNameArr[1]) { return funcNameArr[1]; } else { return 'anonymous'; } } else { throw new Error('That\'s not function'); } } function example(a) { console.log('example function', a); } var d = () => {}; console.log(getFunctionName(example)); console.log(getFunctionName(d)); Правда с точки зрения практики, в этом особого смысла нет. Если вы конечно не собираетесь делать всякие разные проверки на имя входящей функции. Ну и изначальная конструкция с this работать не будет. Так как this это контекст функции, и он обычно не совпадает с самой функцией. |
ksa,
function showName(f){alert(f+'')}; function MyFn(a,b=1,c=7){}; showName(MyFn); Поучил строку: "function MyFn(a,b=1,c=7){}" Какую надо придумать регулярку, чтобы: 1) получить всё что после первого пробела (после "function ") 2) отсечь всё что после первой закрывающей скобки 3) разбить "MyFn(a,b=1,c=7)" на две части: "MyFn" и "(a,b=1,c=7)" 4) из второй части убрать скобки, нап. так: "(a,b=1,c=7)".slice(1,-1) 5) вернуть массив ["MyFn","a,b=1,c=7"] или объект {fn:"MyFn",args:"a,b=1,c=7"} Аргументы я потом получу через split(','). Вот такие мысли. |
function getFunctionName(func) { if(typeof func === 'function') { var funcNameArr = /^\s*(?:function)?\s*([^\(]*)\s*\((.*?)\)(?:\s*\{|\s*\=\>\s*\{?).*/gmi.exec(func.toString()); if(funcNameArr) { return { funcName: funcNameArr[1] || 'anonymous', params: funcNameArr[2] } } } else { throw new Error('That\'s not function'); } } function example(a = {1:2, 2:3}, b=6, {d: d}, c) { console.log('example function', a); } var d = (c = 2, {a}) => {}; function p(a, b=')', d = '{') { function o(){console.log('albert')}} function MyFn(a,b=1,c=7){}; function s(a=')') { } console.log(getFunctionName(example)); // {funcName: "example", params: "a = {1:2, 2:3}, b=6, {d: d}, c"} console.log(getFunctionName(d)); // {funcName: "anonymous", params: "c = 2, {a}"} console.log(getFunctionName(MyFn)); // {funcName: "MyFn", params: "a,b=1,c=7"} console.log(getFunctionName(p)); // {funcName: "p", params: "a, b=')', d = '{'"} console.log(getFunctionName(s)); // {funcName: "s", params: "a=')'"} |
console.log(EmperioAf.isMaster()); // -> true Спасибо! |
Цитата:
Цитата:
var str="function MyFn(a,b=1,c=7){}"; alert(str.substr(9)); Цитата:
var str="function MyFn(a,b=1,c=7){}"; alert(str.split(')')[0]+')'); Цитата:
var str="MyFn(a,b=1,c=7)"; var a=str.split('('); alert(a[0]+' и ('+a[1]); Цитата:
var str="(a,b=1,c=7)"; alert(str.match(/\((.+)\)/)[1]); Цитата:
var str="MyFn(a,b=1,c=7)"; var o={}; alert(str.match(/.*(?=\()/)); o.fn=str.match(/.*(?=\()/); alert(str.match(/\((.+)\)/)[1]); o.args=str.match(/\((.+)\)/)[1]; |
На деле таких задач никогда не должно возникать, а если и возникают - значит Вы делаете что-то не так.
|
ksa,
обалдеть, спасибо! Ruslan_xDD, если только вы не проводите различные опыты ) |
ksa,
'(a,b=1,c=7)'.match(/\((.+)\)/)[1] Это самый короткий способ получить регуляркой то, что внутри скобок? Добавлю, что все функции, которые отправляются в MyFn, будут предварительно сжаты, следовательно обработку пробелов в регулярке можно исключить. Конечная цель получить функцию, которая бы разбивала входящие данные на куски по типам: имя_функции, группа_параметров, свойство, метод. В итоге для каждой полученной функции должен быть создан объект с разложенными по типу кусками. По сути речь идет о Деконструкторе. Вместо функции можно передать любой валидный код и Деконструктор должен разложить ее на части: свойства, методы, циклы и тд. Останется потом только обернуть в теги и раскрасить. Отправляем строку коду, а на выходе красивая сборка из типовых блоков кода. |
Teamur,
Рекомендую изучить исходный код готово парсера js в AST. https://github.com/babel/babylon например. '(a =")", bc)'.replace(/^\((.*)\)$/, '$1') // a =")", bc Более безопасный способ, чтобы у вас код не упал с ошибкой, когда пришла строка без скобок. |
Цитата:
var str="(a,b=1,c=7)"; alert(str.substr(1,str.length-2)); |
ksa,
я думаю через слайс будет короче и проще: var result = "(lorem ipsum dolor)".slice(1,-1) А какая самая простая и короткая регулярка для этого? |
Цитата:
Цитата:
|
ksa,
ага, понял, поверю, спасибо! Удачи! |
Имя функций, которая не является ни геттером ни сеттером и не является результатом функций bind можно узнать вызвав свойство name у функций.
Что же касается способов получения имени функций, и её параметров при помощи регулярного выражения, которые описаны выше в этой теме, то там не учтены некоторые особенности объекта функция (и в частности её метода toString) Поэтому чудовищно хромают. Для начала нужно узнать, является ли объект object функцией. Для этого подойдёт... typeof object === "function" или object instanceof Function function getFunctionData(object) { if(object instanceof Function === false) throw new TypeError("First argument to getFunctionData function must be an Function"); // удаляем из начала имени указания того, что это сеттер, // геттер или получено из функций bind // имя функций объявленное при помощи символа будет в квадратных скобках let name = object.name.replace(/^(?:set|get)\s|(?:bound\s+)+/, ""); return { name } } // помощник function test(fn) { let functionData = getFunctionData(fn); console.group(`Функция — ${functionData.name ? functionData.name : "безымянная"}`); console.log(fn); console.groupEnd(); } // примеры (результаты видны в консоли, F12) test(Function); test(function Awesome() {}); test(class PolyRange extends HTMLInputElement {}); test(new Function("/* I'm really anonymous */")); test(({ id: a }) => () => a); test(async (url) => await fetch(url).then(response => response.json())); test(function* counter() {}); test(class { baz(...nums) { return [...new Set(nums)]; } }); test((class { baz(...nums) { return [...new Set(nums)]; } }).prototype.baz); test(((x = "function you have got confused() {}") => ({[x](x=x){}})[x])()); test(Object.getOwnPropertyDescriptor({set foo({mode} = { mode: "normal" }) {}}, "foo").set); test(function* range({ mapper = function(x) { return x }, start = 0, end = Infinity, step = 1 } = {}) { let value = start; while(end > value) yield mapper(value += step); }); test((function save() {}).bind({})); test((function save() {}).bind({}).bind({}).bind({})); test(({[Symbol.for("Hello")](x){}})[Symbol.for("Hello")]); Теперь давайте получим имена всех параметров функций. При помощи какого-то одного регулярного выражений это не получится сделать, поскольку невозможно отследить правильный порядок скобок. И аргументы и тело могут содержать последовательности символов "=>", ")", "{" или "}". Например, вот какие строки может возвращать метод toString у функций: function() {} (a = a => a) => {} function* (...args) {} class {} (){} // получается путем ({[""](){}})[""]; async *function({url, options: { mode }}) {} function (callback = function(callback = async function(callback) {}) {}) {} Можно использовать парсер, например, Esprima — стандартно-совместимый анализатор ECMAScript — чтобы найти все имена параметров, которые будут доступны после того, как функция будет вызвана. Вот окончательный вариант функций getFunctionData, которая возвращает имя и имена параметров функций... <!doctype html> <html lang="en" style="background: #f06; "> <head> <meta charset="utf-8"> </head> <body> <script src="https://unpkg.com/esprima@4.0.0/dist/esprima.js"></script> <script> function getFunctionData(object) { if(object instanceof Function === false) throw new TypeError("First argument to getFunctionData function must be an Function"); // удаляем из начала имени указания того, что это сеттер, // геттер или получено из функций bind // имя функций объявленное при помощи символа будет в квадратных скобках let name = object.name.replace(/^(?:set|get)\s|(?:bound\s+)+/, ""); // теперь используем Esprima // встроенные функций и wasm всегда возвратят [] // поскольку имеют вид "function() { [native code] }" // и поскольку можно `Function.prototype.toString = () => "function() { [native code] }";` // таким образом не гарантируется получение имён параметров, если toString у // функций был переопределён // вместо этого можно таким же образом разобрать исходный код... let body = object.toString(); let ast = tryThese( () => esprima.parse("(" + body + ");"), () => esprima.parse("(function " + body + ");"), () => esprima.parse("(function " + body.replace(/^function set /, "") + ");"), () => esprima.parse("(function " + body.replace(/^function get /, "") + ");"), () => esprima.parse("(" + body.replace("[native code]", "") + ");"), ); let firstBody = ast.body[0] || {}; let params = firstBody.type === "ExpressionStatement" ? firstBody.expression.type === "ClassExpression" ? firstBody.expression.body.body : firstBody.expression.params : firstBody.params; // возвращает массив всех переменных, которые будут // доступны, когда функция будет вызвана function getParams(ctx, paramNames = []) { if(!ctx) return paramNames; if("type" in ctx === false) { ctx.forEach(param => getParams(param, paramNames)); } else { // рекурсивный обход дерева — поиск узлов типа "Identifier" switch(ctx.type) { case "AssignmentPattern": paramNames.push(...getParams(ctx.left)); break; case "ArrayPattern": paramNames.push(...getParams(ctx.elements)); break; case "FunctionExpression": paramNames.push(...getParams(ctx.params)); break; case "Identifier": paramNames.push(ctx.name); break; case "ObjectPattern": paramNames.push(...getParams(ctx.properties)); break; case "MethodDefinition": if(ctx.kind !== "constructor") break; case "Property": paramNames.push(...getParams(ctx.value)); break; case "RestElement": paramNames.push(...getParams(ctx.argument)); break; } } return paramNames; } // возвращает результат той функций, которая не улетает function tryThese(...fns) { let fn, error; while(fn = fns.shift()) { try { return fn(); } catch (_) { error = _; continue; } } throw error; } return { name, params: getParams(params) } } // помощник function test(fn) { let functionData = getFunctionData(fn); console.group(`Функция — ${functionData.name ? functionData.name : "безымянная"}`); console.log(fn); console.log("Параметры: ", JSON.stringify(functionData.params)); console.groupEnd(); } // примеры (результаты видны в консоли, F12) test(Function); test(function Awesome() {}); test(function Point(x, y) {}); test(class Point { constructor(x, y) {}}); test(class PolyRange extends HTMLInputElement {}); test(new Function("[a, b = \"\", ...c]", "/* I'm really anonymous */")); test(({ id: a }) => () => a); test(async (url) => await fetch(url).then(response => response.json())); test(function* counter() {}); test(class { baz(...nums) { return [...new Set(nums)]; } }); test((class { baz(...nums) { return [...new Set(nums)]; } }).prototype.baz); test(((x = "function you have got confused() {}") => ({[x](x){}})[x])()); test(class extends (class extends (class extends (class extends (function ({ href } = location) {}) {}) {}) {}) {}); test(function whois({age, displayName: displayName, fullName: {firstName: name}}){}); test(Object.getOwnPropertyDescriptor({set foo({mode} = { mode: "normal" }) {}}, "foo").set); test(Object.getOwnPropertyDescriptor({get foo() {}}, "foo").get); test(function* range({ mapper = function(x) { return x }, start = 0, end = Infinity, step = 1 } = {}) { let value = start; while(end > value) yield mapper(value += step); }); test((function save() {}).bind({})); test((function save() {}).bind({}).bind({}).bind({})); test(({[Symbol.for("Hello")](x){}})[Symbol.for("Hello")]); test(({a1: a2 = a3 => a4}) => {}); </script> <p style="font: 2.5em / 1.5 Menlo, monospace; text-align: center; text-shadow: 0 0.1em 0.1em #7b3755; ">результаты видны в консоли, F12</p> </body> </html> Esprima http://esprima.org/demo/parse.html Конечно же Вы можете анализировать всё, что угодно, а не только параметры функции... |
Malleys,
мда, Esprima - серъезная штука, спасибо! Теперь я всё смогу и я победю )) |
Часовой пояс GMT +3, время: 08:28. |