Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Можно ли узнать имена входящих аргументов функции? (https://javascript.ru/forum/misc/16838-mozhno-li-uznat-imena-vkhodyashhikh-argumentov-funkcii.html)

FINoM 25.04.2011 02:14

Можно ли узнать имена входящих аргументов функции?
 
Можно ли узнать, что в функцию
function(foo,bar){
   ...
}
переданы аргументы 'foo', 'bar'?

Riim 25.04.2011 03:52

PrototypeJs:
Function.prototype.argumentNames = function() {
  var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
    .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
    .replace(/\s+/g, '').split(',');
  return names.length == 1 && !names[0] ? [] : names;
};

x-yuri 28.04.2011 08:01

и что, действительно бывает нужно такое выяснять?

FINoM 29.04.2011 03:29

Цитата:

Сообщение от x-yuri
и что, действительно бывает нужно такое выяснять?

Нужно для функции определить дефолтные значения аргументов, например:
function valera(x, y, z) {
    defaultArgs(arguments, {y: 5, z: 10});
    ...
}
C методом, который предложил Riim это достаточно просто. Еще раз спасибо.

x-yuri 30.04.2011 07:43

советую подумать, почему так никто не делает... и, если будем развивать тему, приведи какой-нибудь конкретный пример

melky 30.04.2011 15:05

Цитата:

Сообщение от FINoM (Сообщение 102577)
Нужно для функции определить дефолтные значения аргументов, например:
function valera(x, y, z) {
    defaultArgs(arguments, {y: 5, z: 10});
    ...
}
C методом, который предложил Riim это достаточно просто. Еще раз спасибо.


по-моему,так проще!

function valera(x, y, z) {
    y=y || 5, z = z || 10;
    ...
   // или так
  y = typeof y === 'undefined' ? 5 : y ;
  z = typeof z === 'undefined' ? 10 : z ;
}

FINoM 30.04.2011 22:03

x-yuri, melky, так красивее.
Цитата:

Сообщение от x-yuri
почему так никто не делает

Ты всех программистов в лицо знаешь? :)

poorking 30.04.2011 23:03

FINoM,
Цитата:

Сообщение от FINoM
x-yuri, melky, так красивее.

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

Тем более что вам это даст, имена переменных? ну передали вы в функцию defaultArgs ряд arguments и хэш имя>значение,
в этой функции вы будете изменять arguments, но имена-то зачем, ведь в arguments значения по Number индексу хранятся, можно как нибудь так defaultArgs(arguments, 5, 6, 7), внутри первый_аргумент[0] = arguments[1] и так до конца arguments, хотя все равно не понятно зачем это

B@rmaley.e><e 30.04.2011 23:36

Цитата:

Сообщение от poorking
ну передали вы в функцию defaultArgs ряд arguments и хэш имя>значение,
в этой функции вы будете изменять arguments, но имена-то зачем, ведь в arguments значения по Number индексу хранятся

Вот для этого и нужно определить, как называется первый аргумет, как второй и т.д.

poorking 30.04.2011 23:56

а почему вот так нельзя
function defaultArgs(/* arguments */){
	var len = arguments.length - 1;
	
	for(var i = 0; i < len; i ++){
		arguments[0][i] = arguments[i + 1];
	}
}
	
function valera(x, y, z){
	defaultArgs(arguments, 5,6,7);
	/* code */
}


Потому что не красиво?

PeaceCoder 01.05.2011 00:32

Чем бы программист не страдал главное что бы пальцы были прямые...

x-yuri 01.05.2011 07:27

Цитата:

Сообщение от FINoM
Ты всех программистов в лицо знаешь?

переубеди меня, я тебе спасибо скажу ;)

Цитата:

Сообщение от poorking
Обычно оптимизируют в сторону производительности, а вы в сторону красивости,

обычно пишут понятный код, а потом оптимизируют узкие места

Цитата:

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

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

есть 2 способа указать значения по умолчанию: 1) arg || '...', 2) options = options || {'option-1': '...'}

melky 01.05.2011 10:17

Цитата:

Сообщение от poorking
Обычно оптимизируют в сторону производительности, а вы в сторону красивости

Цитата:

Сообщение от x-yuri (Сообщение 102830)
обычно пишут понятный код, а потом оптимизируют узкие места

вы о разном говорите, но оба правы )

да и разницы нет,как писать и что вызывать

все равно внутри....

defaultArgs(arguments, {y: 5, z: 10});

какой-нибудь цикл for-in, который на каждой итерации проверяет typeof со строкой. обычно это не так уж и долго...

но время теряется.

и в веб-приложениях, в которых даже деление на 1000 заменяют на умножение на 0.001,потому что так на 50 микросекунд быстрее ( в играх, например )..

.. оно просто растрачивается. ради красивости кода.


ээ

это какой-то холивар

можно еще поговорить,как лучше в функцию передавать аргументы -

через объект,

foo ( { bar : 'ololo', one : 'two' } );



или через перечисление

foo ( 'ololo', 'two' );

B@rmaley.e><e 01.05.2011 11:15

Цитата:

Сообщение от melky
и в веб-приложениях, в которых даже деление на 1000 заменяют на умножение на 0.001,потому что так на 50 микросекунд быстрее ( в играх, например )..

Глупая оптимизация. Можно подумать, в наш век едва ли не повсеместного распространения JIT компиляции, интерпретатор не додумается заменить деление на умножение.
Цитата:

Сообщение от melky
можно еще поговорить,как лучше в функцию передавать аргументы

Вот статейка по теме.
Цитата:

Сообщение от poorking
а почему вот так нельзя

Для начала, пожалуй, потому, что так не работает. В Опере, например, непереданные аргументы не появятся сами собой после изменений значений в arguments.
(function(a, b, c, d){
  alert([a,b,c,d] + '\n' + arguments.length);
  arguments[0] = 10;  arguments[1] = 13;
  arguments[2] = 11;  arguments[3] = 12;
  arguments.length = 4;
  alert([a,b,c,d] + '\n' + arguments.length);
})(5, 4)
Смотреть в опере.

Да и устанвливать значения по-умолчанию внутри самой функции как-то не круто, на мой взгляд. Перед установкой значений по-умолчанию можно написать еще кучу кода и не заметить этого. Мне больше нравится такой подход (в качестве бонуса можно еще и контекст указать)
function lambda(fnc, context, defaults){ // другого имени не придумал
	var defaultArgs = [], key, names;
	if(defaults){
		names = fnc.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
		                      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
		                      .replace(/\s+/g, '').split(',');
		names = names.length == 1 && !names[0] ? [] : names;

		for(var i = 0, length = names.length; i < length; ++i){
			key = names[i];
			defaultArgs.push(defaults[key]);
		}
	}

	return function(){
			return fnc.apply(context, lambda.merge(arguments, defaultArgs));
		}
}
lambda.merge = function(arr1, arr2){
	var length = Math.max(arr1.length, arr2.length),
		arr = [];
	for(var i = 0; i < length; ++i){
		arr.push(i < arr1.length ? arr1[i] : arr2[i]);
	}
	return arr;
};


alert(lambda(function(a, b){alert(this); return a + b}, 'Hello, world!', {a: 50, b: 77, c: 42})(10));

poorking 01.05.2011 15:32

Это уже интереснее, но я все же склоняюсь к тому чтобы не разбирать по именам аргументы
function merge(arr1, arr2){
	var length = Math.max(arr1.length, arr2.length),
		arr = [];
	for(var i = 0; i < length; ++i){
		arr.push(i < arr1.length ? arr1[i] : arr2[i]);
	}
	return arr;
}

function withDefaults(fn, defaults, cnx){
	if(!defaults){
		defaults = [];
	}
	if(!cnx) {
		cnx = this;
	}
	
	return function(/* args for fn*/){
		return fn.apply(cnx, merge(arguments, defaults));
	}
		
}
	
function mul(a, b){
	alert(a + "*" + b + " = " + a*b);
}
	
withDefaults(mul, [3,5])(5,7);
withDefaults(mul, [3,5])();
withDefaults(mul, [3,5])(6);

B@rmaley.e><e 01.05.2011 16:43

poorking, тут уж, как говорится, на вкус и цвет. Хотите - передавайте массив, хотите - хеш. У меня, как не трудно заметить, хеш все равно на этапе препроцессинга перегоняется в массив.

Единственный случай, где я вижу разумным использование хеша вместо массива - указание значений по-умолчанию для произвольных переменных, а не только первых n штук. Но в таком случае есть 2 минуса: нам нужно ввести какое-то значение, которое будет сообщать, что данный аргумент не задан и, что гораздо существеннее, при вызове такой функции придется писать что-то вроде
fncName(a, b, undefined, undefined, 'red');
Это, все-таки, не очень хорошо и в статье, ссылку на которую я привел в предыдущем сообщении, предлагается в таких случаях использовать один аргумент для функции - объект с нужными полями.
fncName({
  speed: a,
  count: b,
  codeName: 'red'
});

poorking 01.05.2011 17:13

Статью конечно посмотрел, и я тоже считаю что это самый правильный вариант, я этим часто пользуюсь - и просто и красиво, да еще и удобно, и код читается легко и так далее..
Object.prototype.set = function(props){
	for(var any in props) if(props.hasOwnProperty(any)){
		if(typeof props[any] === "object" && !props[any] instanceof Array){
			if(!this[any]){
				this[any] = {};
			}
			this[any].set(props[any]);
		}else{
			this[any] = props[any];
		}
		
	}
	//для удобства
	return this;
}
var a = {}.set({codename: "red", value: 5, style: {color: "red", border: "1px solid red"}, fields: [1,2,3,4,5,6,7,8]});

//смотреть в FF
alert(a.toSource());
a.set({some: true});
alert(a.toSource());

Очень удобно, кто не любит расширять прототипы родных классов, в эту функцию можно передавать самого виновника

А еще лучше так чтобы если массив передаем в качестве устанавливаемого значение, чтобы не ссылка его устанавливалась а копия, если например набор параметров един, но изменяется по ходу кода
Array.prototype.copy = function(){
	var len = this.length, nuarr = [];
	for(var i = 0; i < len; i ++){
		if(i in this){
			if(typeof this[i] === "object" && !this[i] instanceof Array){
				nuarr[i] = {}.set(this[i]);
			}else if(this[i] instanceof Array){
				nuarr[i] = this[i].copy();
			}else{
				nuarr[i] = this[i];
			}
		}
	}
	
	return nuarr;
}

Object.prototype.set = function(props){
	for(var any in props) if(props.hasOwnProperty(any)){
		if(typeof props[any] === "object" && !props[any] instanceof Array){
			if(!this[any]){
				this[any] = {};
			}
			
			this[any].set(props[any]);
		}else if(props[any] instanceof Array){
			this[any] = props[any].copy();
		}else{
			this[any] = props[any];
		}
		
	}
	//для удобства
	return this;
}

Надеюсь не накосячил в коде почти не тестил

x-yuri 02.05.2011 16:25

Цитата:

Сообщение от melky
Обычно оптимизируют в сторону производительности, а вы в сторону красивости

Цитата:

Сообщение от melky
вы о разном говорите, но оба правы )

отнюдь, poorking вообще что-то странное сказал. Сначала он с потолка утверждает, что ТС оптимизирует. И всем становится понятно, что это какой-то нелепый способ оптимизации... если не обратить внимание, что это ни разу не оптимизация. Писать читабельный код - это хорошо, пока ты не начинаешь добавлять в язык новые конструкции

Цитата:

Сообщение от B@rmaley.e><e
// другого имени не придумал

это знак ;)

и все эти ваши решения ни чем не лучше стандартного подхода, если не хуже. Кстати, как так получилось, что значения по умолчанию оказались вне функции?

Цитата:

Сообщение от FINoM
x-yuri, melky, так красивее.

а если нужна именно красота, учи haskel, ml

B@rmaley.e><e 02.05.2011 17:35

Цитата:

Сообщение от x-yuri
Кстати, как так получилось, что значения по умолчанию оказались вне функции?

Этого я и добивался. Что в C, что в php значения по-умолчанию задаются вне тела функции. Да, там они задаются в самом объявлении аргументов, но достичь этого в JS, понятное дело, не представляется возможным. Целью было обеспечение гарантированной установки значения по-умолчанию, т.е. так, чтобы уже на входе в функцию мы имели дело либо с настоящими значениями, либо стандартными. Таким образом, у Вас нет возможности прострелить себе ногу, попытавшись использовать неинициализированную переменную или случайно удалив ее инициализацию.

Но в целом, да, особой пользы от такого подхода нет.

poorking 02.05.2011 19:04

Цитата:

Сообщение от x-yuri
отнюдь, poorking вообще что-то странное сказал. Сначала он с потолка утверждает, что ТС оптимизирует

это была ирония

melky 02.05.2011 19:38

Цитата:

Сообщение от B@rmaley.e><e (Сообщение 103018)

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

а если делать так?


function arr_cycle ( arr, func ) {

// явл. ли массивом
if ( arr.constructor !== Array ) throw new Error( "'arr' must be Array" );

for ( var i = 0; i < arr.length; i++ )
           i in arr &&  func.apply(window, [ arr[i], i, arr ] );
 
}


правда, очень жаль, что изза такой типизации в JS нельзя использовать оверлоадинг ( перегрузки )

приходится внутри всей функции проверять тип аргумента и только потом расфасовывать методы

B@rmaley.e><e 02.05.2011 19:57

melky, и что это? Во-первых, где тут значения по-умолчанию, а во-вторых, что помешает Вам написать какой-нибудь код, оперирующий с arr, до проверки?

melky 02.05.2011 20:01

вот значения по-умолчанию. про это забыл

...
if ( arr = arr || [] ).constructor  ...


но если ведь необходимо оперировать только с массивами, то как еще ?

B@rmaley.e><e 02.05.2011 20:40

Это и есть стандартный подход.

x-yuri 02.05.2011 21:18

Цитата:

Сообщение от B@rmaley.e><e
Этого я и добивался. Что в C, что в php значения по-умолчанию задаются вне тела функции.

нет, я говорил "вне функции". С таким подходом надо создать обертку для каждой функции и использовать функцию только через нее (чтобы не дублировать значения по умолчанию):
function f(a, b){alert(this); return a + b}

function fWrapper(){
    return lambda(f, 'Hello, world!', {a: 50, b: 77, c: 42});
}

к тому же lambda навязывает задание контекста, т.е. контекст для методов надо либо жестко прописывать в обертке (но тогда нельзя будет вызвать метод в другом контексте), либо указывать при каждом вызове

Цитата:

Сообщение от B@rmaley.e><e
Целью было обеспечение гарантированной установки значения по-умолчанию, т.е. так, чтобы уже на входе в функцию мы имели дело либо с настоящими значениями, либо стандартными.

ну вот я и говорю, что вы пытаетесь изменить язык. Такие вещи вызывают много проблем, и для этого нужны веские причины. А для надежности нужно тестировать код

Цитата:

Сообщение от B@rmaley.e><e
а во-вторых, что помешает Вам написать какой-нибудь код, оперирующий с arr, до проверки?

а что помешает сделать ошибку или опечатку в другом месте? Например, вызвать функцию с другими значениями по умолчанию или забыть воспользоваться оберткой

Цитата:

Сообщение от melky
правда, очень жаль, что изза такой типизации в JS нельзя использовать оверлоадинг ( перегрузки )

приходится внутри всей функции проверять тип аргумента и только потом расфасовывать методы

во-первых не жалко, во-вторых, можешь привести конкретный пример, где по-твоему нужна перегрузка?

B@rmaley.e><e 02.05.2011 21:32

Цитата:

Сообщение от x-yuri
к тому же lambda навязывает задание контекста, т.е. контекст для методов надо либо жестко прописывать в обертке (но тогда нельзя будет вызвать метод в другом контексте), либо указывать при каждом вызове

Это особенность текущей реализации, которая не имеет отношения к аргументам по-умолчанию.
Цитата:

Сообщение от x-yuri
нет, я говорил "вне функции". С таким подходом надо создать обертку для каждой функции и использовать функцию только через нее (чтобы не дублировать значения по умолчанию):
function f(a, b){alert(this); return a + b}

function fWrapper(){
    return lambda(f, 'Hello, world!', {a: 50, b: 77, c: 42});
}

К чему этот код? Вы собираетесь использовать одну функцию с разными аргументами по-умолчанию? Функция сразу должна быть создана через lambda.
Цитата:

Сообщение от x-yuri
а что помешает сделать ошибку или опечатку в другом месте? Например, вызвать функцию с другими значениями по умолчанию или забыть воспользоваться оберткой

Функцию Вы объявляете один раз, а ее тело может правиться много и долго. Как забыть обертку - не понял.

melky 02.05.2011 21:36

Цитата:

Сообщение от x-yuri (Сообщение 103079)
можешь привести конкретный пример, где по-твоему нужна перегрузка?

если придумать код, а потом попробовать перевести его на JS, то решить можно все. и правда.

но передавать объекты ,как один аргумент, будет удобнее

Код:


function doetc( string a, int b, float c, element d ) {
  /* code */
}

function doetc( object options ) {
  doetc( options.a, options.b, options.c, options.d );
}

//example
function hide( element a, int b ){
  doetc (  "hide", b, 0.0, a);
}


x-yuri 02.05.2011 22:42

Цитата:

Сообщение от B@rmaley.e><e
Это особенность текущей реализации, которая не имеет отношения к аргументам по-умолчанию.

ну извини, я как-то расчитывал, что ты приводишь готовый к использованию код. А решить да, можно, надо чтобы lambda сохраняла контекст. И, кстати, это лишняя функциональность в lambda. Это следует хотя бы из ее описания: она назначает значения по умолчанию и задает контекст. У нее слишком много обязанностей. И это плохо.

Цитата:

Сообщение от B@rmaley.e><e
К чему этот код? Вы собираетесь использовать одну функцию с разными аргументами по-умолчанию? Функция сразу должна быть создана через lambda.

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

B@rmaley.e><e, давай лучше на ты ;)

melky, у тебя абстрактный пример, а я просил конкретный


Часовой пояс GMT +3, время: 19:25.