рекурсивная конкатенация аргументов функций JavaScript - PullRequest
5 голосов
/ 01 февраля 2009

Я наткнулся на головоломку javascript, спрашивающую: Напишите фрагмент кода JavaScript, состоящий из одной строки, который объединяет все строки, переданные в функцию:


    function concatenate(/*any number of strings*/) {
      var string = /*your one line here*/
      return string;
    } 

@ meebo

Видя, что аргументы функции представлены в виде индексированного объекта, МОГУТ быть массивом, я подумал, что это можно сделать рекурсивным способом. Однако моя рекурсивная реализация выдает ошибку. - "conc.arguments.shift не является функцией" -


    function conc(){
        if (conc.arguments.length === 0) 
            return "";
        else 
            return conc.arguments.shift() + conc(conc.arguments);
}

кажется, что conc.arguments не является массивом, но может быть доступен по числовому индексу и имеет свойство длины ??? сбивает с толку - пожалуйста, поделитесь мнениями и другими рекурсивными реализациями.

Спасибо

Ответы [ 5 ]

12 голосов
/ 01 февраля 2009

arguments называется объектом, подобным массиву. Как вы уже видели, вы можете получить доступ к его элементам по индексу, но у вас нет всех методов Array. Другими примерами Array-подобных объектов являются HTML-коллекции, возвращаемые getElementsByTagName () или getElementsByClassName (). jQuery, если вы когда-либо использовали его, также является объектом, похожим на массив. После запроса некоторых объектов DOM, проверьте получившийся объект jQuery с помощью Firebug на вкладке DOM, и вы поймете, что я имею в виду.

Вот мое решение проблемы Meebo:

function conc(){
    if (arguments.length === 0)
        return "";
    else
        return Array.prototype.slice.call(arguments).join(" ");
}

alert(conc("a", "b", "c"));

Array.prototype.slice.call(arguments) - хороший прием для преобразования нашего arguments в настоящий объект Array. В Firefox Array.slice.call(arguments) будет достаточно, но он не будет работать в IE6 (по крайней мере), поэтому обычно используется предыдущая версия. Кроме того, этот прием не работает для коллекции, возвращаемой методами DOM API в IE6 (по крайней мере); это выдаст ошибку. Кстати, вместо call можно было бы использовать apply.

Небольшое объяснение об объектах, подобных массиву. В JavaScript вы можете использовать практически что угодно для именования членов объекта, и числа не являются исключением. Таким образом, вы можете сконструировать объект, который выглядит следующим образом, который является совершенно допустимым JavaScript:

var Foo = {
    bar : function() {
        alert('I am bar');
    },

    0 : function() {
        alert('I am 1');
    },

    length : 1
}

Вышеуказанный объект является объектом, подобным массиву, по двум причинам:

  1. У него есть члены, имена которых являются числами, поэтому они похожи на индексы Array
  2. Он имеет свойство length, без которого вы не можете преобразовать объект в настоящий массив с помощью конструкции: Array.prototype.slice.call(Foo);

Объект arguments объекта Function во многом похож на объект Foo, только в том, что он имеет свое специальное назначение.

7 голосов
/ 01 февраля 2009

Мозилла на тему :

Объект arguments не является массивом. Это похож на массив, но не имеет никакого массива свойства кроме длины. Например, у него нет метода pop. Однако это может быть преобразовано в реальный Массив:

var args = Array.prototype.slice.call(arguments);

Поэтому решение вашей проблемы довольно простое:

var string = Array.prototype.slice.call(arguments).join("");

Кстати: далее говорится:

Объект аргументов является локальным переменная доступна во всех функции; аргументы как свойство Функция больше не может быть использована.

Вы должны использовать только arguments вместо func.arguments

6 голосов
/ 01 февраля 2009

Это работает:

function concatenate(){
    return [].join.call(arguments, "");
}
alert(concatenate("one", "two", "three"));
1 голос
/ 01 февраля 2009

Вы можете сделать это:

function concatenate() {
    if (arguments.length > 1) {
        return arguments[0] + concatenate.apply(this, Array.prototype.splice.call(arguments, 1));
    }
    return arguments[0];
}
1 голос
/ 01 февраля 2009

Список аргументов не является подлинным массивом. Я думаю, что вы можете заимствовать методы массива и использовать их в аргументах "call" или "apply".

...