Как стать автором
Обновить

Комментарии 37

полезная вещь, буду пользоваться. спасибо!
Array.prototype.slice.call можно вызывать и без 0:
Array.prototype.slice.call(arguments);
Вот этот код дефолтных значений аргументов:
function (foo, bar) {
if (typeof foo === 'undefined') foo = 30;
if (typeof bar === 'undefined') bar = 'test';

console.log(foo, bar);
}


Можно написать гораздо проще (:
function (foo, bar) {
foo = foo || 30;
bar = bar || 'test';

console.log(foo, bar);
}
>>foo || 30. В этом случае у вас foo = 0 пролетает.
Да, не учел. Но для str или array подходит (:
тогда короче foo ||=30;
SyntaxError: syntax error ;)
блин не интересно, в ActionScript работает
а если в качестве foo 0 передать он не станет 30?
никогда так не делайте.
0, '', false приведут к фейлу.
К сожалению, из-за бага в трёх популярных браузерах(IE, Fx, Opera) я не смог добиться желаемого эффекта

О каком баге идет речь? По-моему, наоборот упомянутые браузеры более строго соответствуют спецификациям ECMA.
function test(a, b) {
    arguments[0] = 100;
    arguments[1] = 101;
    console.log(a,b);
}
test(1,2); // => 100, 101
test(1); // => 100, undefined

Потому как динамическая связь устанавливается только для реально переданных аругментов:
(...) named data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function’s execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. (...)

До этой фразы идет подробное описание как строить этот объект «arguments» — формальные аргументы не переданные фактически в «arguments» не попадают.
Да, увидел в посте ссылку на баг в файерфоксе. Во-первых, он давно уже пофиксан, а во-вторых, он совершенно не имеет отношения к вашему коду.
Я рад, что затеяли это обсуждение, очень хотел бы определится, кто таки неправ из браузеров.
На самом деле я спецификацию читал и, признаюсь, немного в ней запутался, но позвольте проясню.
Если вы не против, будем обсуждать перевод.

У функции есть аргументы формальные и актуальные. Если создавать функцию через конструктор:
new Function('a', 'b', 'return a+b');


То формальные аргументы — это «a» и «b». (и они не зависят от того, как функцию вызывали):
Если более чем один параметр передаётся конструктору Function, все параметры кроме последнего преобразовываются в строки и конкатенируются вместе с использованием запятых в качестве разделителя. Результирующая строка интерпретируется как СписокФормальныхАргументов для ТелаФункции, определённого последним параметром.


Я сходу не нашел такое же определение для создания функции не-через-конструктор, но, можно логически предположить, что оно не отличается. Значит, формальные аргументы — это аргументы, объявлены в загоовку функции. Теперь идем дальше:

Для каждого неотрицательного числа arg, меньшего значения свойства length создаётся свойство с именем ToString(arg) и атрибутом { DontEnum }. Начальным значением этого свойства является реальное значение соответствующего аргумента, переданное при вызове. Первое реальное значение аргумента соответствует arg = 0, второе — arg = 1 и так далее. В том случае, когда arg меньше количества формальных параметров объекта Function, значение свойства является общим с соответствующим свойством объекта активации. Это означает, что изменение данного свойства изменяет соответствующее значение свойства у объекта активации и наоборот.


Попробую написать псевдокод:
for (arg = 0; arg < func.length; arg++) {
    link(arguments[i], formalParameters[i]);
}


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

Так я понял этот момент. Поправьте меня, если где-то ошибся.

Ссылку на баг дал не просто так. На mdc написано:
Note: The SpiderMonkey JavaScript engine has a bug in which arguments[n] cannot be set if n is greater than the number of formal or actual parameters. This has been fixed in the engine for JavaScript 1.6.

Согласен, баг другой, раньше, я так понял не менялось даже значение arguments[i], но в 4 часа ночи они показались очень похожими)
Почитайте тут на странице 60. Ваш псевдокод должен быть:
for (var i = arguments.length; i--; ) {
    link(arguments[i], formalParameters[i]);
}
Согласен. Вроде, согласно ES5 оно должно считаться от аргументов.
Просто любопытно, как считаете, согласно ES3 — я все правильно понял?
Понял, в чем ваше заблуждение:
Для каждого неотрицательного числа arg, меньшего значения свойства length создаётся свойство с именем (...)

Здесь имеется ввиду свойство length созданное на предыдущем шаге. Т.е. не свойство функции, а свойство только что созданного оьъекта arguments. В ES3 описание просто немного более расплывчатое, но, по-моему, все равно вполне однозначное.
Точно, вы правы:
Создаётся свойство с именем length и атрибутами { DontEnum }. Начальным значением этого свойства является число реальных значений аргументов, переданное при вызове.
Описание семантики объекта arguments в ES5/non-strict ES5 JS кодом: gist.github.com/539974

В Хроме помимо упоминавшегося бага, был еще баг с удалением индексов arguments.

Так же, учтите, что в strict-ES5 аксессор для индексов arguments больше не создается (т.е. обычные статические копии формальных параметров).

И, касательно Harmony (aka ES6 или ES.next) arguments вообще будет удален и заменен на rest — полноценный массив.
Да, вы правы — баг именно в Chrome :)
Баг, но какой приятный!
По-моему, этот фукнционал уместнее расположить в прототипе фукнции.

Чтобы типа
var myFunc = function(foor, bar){ console.log(arguments); }.types(Foo, Bar).cast(Foo, Bar).allRequired();
А allRequired как сделать, тем более ему передастся функция, которая требует 0 аргументов?)
Проблема этого подхода в том, что при относительно больших функциях не видно, что происходит с аргументами. Если изучать верх функции, можно пропустить, скажем, кастинг или дефолтные значения.
Function.prototype.allRequired = function(){
  var f = this, validLength = f.length;
  return function(){
    if (arguments.length >= validLength)
      return f.call(this, arguments);
    else
      throw new Error('oh Shit..');
  }
};


Или я вопрос не правильно понял?
только там не .call, а .apply.
(function () {
	// code
}.cast(Number).allRequired());

Function.prototype.cast = function (types) {
	var fn = this;
	return function () { // cast inner function
		// casting arguments
		return fn.call(this, arguments);
	};
};

Function.prototype.allRequired = function () {
	var fn = this; // оно ссылается не на нужную функцию, а на cast inner function
	return function () {
		
	}
};
Хотя, возможно, можно делать как-то так:
Function.prototype.cast = function (types) {
	var fn = this;
	var result = function () { // cast inner function
		// casting arguments
		return fn.call(this, arguments);
	};
	result.length = fn.length;
	return result;
};
Неа, так не работает.

Если только делать
result._length = fn._length || fn.length;

в каждой обертке.
Вот еще придумался вариант с сохранением нативного length: gist.github.com/934203

Я его не проверял, но вроде должен быть рабочий.
лучше враппер использовать — тогда можно в зависимости от типов и числа параметров разные функции вызывать, а также проверять результат исполнения функции

var find= Types
( HTMLElement
, [ String ]
, fucntion( id ){ return document.getElementById( id ) }
)

var check= Types
( HTMLElement
, [ HTMLElement ]
, fucntion( elem ){ elem }
)

var $= Poly( find, check )
Часто, когда необходимы значения по умолчанию — в функцию передается объект.

function test(options)
{
if(options.foo===default)options.foo=30;
if(options.bar===default)options.bar='test';
console.log(options.foo,options.bar);
}


Для такого случая тоже можно написать аналог вашей функции, устанавливающей значение по умолчанию — и у такого подхода есть свои очевидные преимущества.
В strict mode такое работать уже не будет, т.к. объект arguments не будет иметь связи с формальными параметрами, соответственно даже переданный аргумент нельзя изменить.
Чтобы использовать обертку надо привыкнуть к ней, внедрить, научить других — все как правило очень долго. Мне обычный подход милее и он не менее нагляднее, да и если ещё снабдить код хорошим JSdoc'ом с описанными дефалтными значениями, который понимает любой современный IDE, то будет совсем хорошо (улучшу немного код omfg):
/**
 * Pewpew
 * 
 * @param {Object}   [foo]
 * @param {Mixed}    [foo.smth]
 * @param {Boolean}  [bar=true]
 * @param {String}   [baz='pewpew']
 * @param {Boolean}  [bar=false]
 *
 * @returns {Mixed}
 */
function pewpew(foo, bar, baz, qqq) {
    foo = foo || {};           // default empty object
    bar = bar || bar == null;  // default true (можно не использовать typeof bar)
    baz = baz || 'pewpew';     // default some string
    qqq = qqq || false;        // default false

    if (foo.smth) {
        do();
    }
}

Ну и не забываем, что грядет "strict mode"
Добрый день, подскажите где можно прочитать про strict mode на русском и что это вообще?
Спасибо за полезную информацию.
«strict mode» — это да.
подход такой — не совсем правильный, что делать, если я хочу в качестве строки baz передать пустую строку?

Хотя, конечно, можно писать так:
/**
 * Pewpew
 * 
 * @param {Object}   [foo]
 * @param {Mixed}    [foo.smth]
 * @param {Boolean}  [bar=true]
 * @param {String}   [baz='pewpew']
 * @param {Boolean}  [bar=false]
 *
 * @returns {Mixed}
 */
function pewpew(foo, bar, baz, qqq) {
    foo = foo != null ? foo : {};        // default empty object
    bar = bar != null ? bar : true;      // default true
    baz = baz != null ? baz : 'pewpew';  // default some string
    qqq = qqq != null ? qqq : false;     // default false

    if (foo.smth) {
        do();
    }
}



А в таком коде уже было бы красиво:
/**
 * Pewpew
 * 
 * @param {Object}   [foo]
 * @param {Mixed}    [foo.smth]
 * @param {Boolean}  [bar=true]
 * @param {String}   [baz='pewpew']
 * @param {Boolean}  [bar=false]
 *
 * @returns {Mixed}
 */
function pewpew(foo, bar, baz, qqq) {
    Args(arguments).defaults({}, true, 'pewpew', false)

    if (foo.smth) {
        do();
    }
}



Хотя, в целом, с аргументами согласен.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации