5

Всем привет. Разрешите задать нубский вопрос по CALL/APPLY, может кто-то разжуёт Как следует из многих туториалов и учебников call отличается от apply тем, что в apply аргументом можно передать массив. А в call нет.

var object = {
    "arr": ["Первый элемент",2,3,4,5],
    "func": function() {
        function awayFromMe(arr){
            console.log(arr);
        }
    awayFromMe.call (this, this.arr);
    awayFromMe.apply(this, this.arr);
    }
};
object.func();

То-есть я рассчитывал что call ничего не даст, a apply отработает, но вопреки моим ожиданиям call дал вывод массива:["Первый элемент", 2, 3, 4, 5] apply дал вывод только первого элемента: "Первый элемент"

Почему так происходит?

Qwertiy
  • 123,725

3 Answers3

5

foo.apply(thisArg, argsArray)
foo.call(thisArg, arg1, arg2, ...)

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

Т.е. разница только в том, что в apply вы передаёте аргументы в виде массива, а в call как есть:

function test(a, b, c, d) {
  console.log(a, b, c, d);
}
// Последующие вызовы идентичны
test.apply(null, [1, 2, 3, 4]);
test.call(null, 1, 2, 3, 4);

Другой пример:

let a = [1, 2, 3];

// Один аргумент - массив console.log.call(console, a); // [1, 2, 3]

// Три аргумента console.log.apply(console, a); // 1, 2, 3

// функция суммирует свои аргументы const sum = (...args) => args.reduce((c, a)=> c += a, 0);

console.info('call', sum.call(null, a)); // sum([1,2,3]) = 0+'1,2,3' console.info('apply', sum.apply(null, a)); // sum(1,2,3) = 6

vp_arth
  • 27,179
  • А что это даёт то? Какая разница как передавать? Я же могу массив и в call передать, а в функции вытащить его через arguments – Денис Иванов Feb 17 '17 at 17:50
  • 2
    call вы используете, когда у вас есть все аргументы по отдельности. Когда есть массив неизвестной длины, у вас это не выйдет. Для этого есть apply – vp_arth Feb 17 '17 at 17:54
  • 1
    В новых спецификациях ecmascript можно развернуть массив в раздельные аргументы: foo.call(null, ...arr1, ...arr2). Функция будет вызвана со всеми элементами обоих массивов. – vp_arth Feb 17 '17 at 17:56
  • Что значит массив неизвестной длины? Массив формирующийся динамически, например при использовании Array.fiter? `var object = { "arr": [1,2,3,4,5,6,7,8,9,10], "func": function() { function awayFromMe(){ console.log(arguments); //[Array[6]] }
        awayFromMe.call(this, this.arr.filter(function(elem){if(elem>=5)return elem}));
    }
    

    }; object.func(); ` И ведь все равно работает ...

    – Денис Иванов Feb 17 '17 at 18:11
  • Что значит работает? Вы передали один аргумент - массив. А функция может хотеть несколько, по раздельности – vp_arth Feb 17 '17 at 18:15
  • А можно небольшой пример, пожалуйста. А то я могу call'ом хоть 100 массивов передать сотней аргументов. Вытащить через arguments – Денис Иванов Feb 17 '17 at 18:17
  • Добавил другой пример – vp_arth Feb 17 '17 at 18:24
  • 2
    @ДенисИванов, вытащить через arguments ты можешь только если это твоя функция. и то, нужно будет проверять пришел ли массив потому что вызвали через call, или это просто в качестве первого аргумента передали массив. Если же функция не твоя, то call с массивом может быть не применим. Просто пример функция $.when принимает список объектов, а не массив. Поэтому если ты не знаешь сколько у тебя будет deferred объектов, единственный способ вызвать ее и получить корректный результат - использовать apply. Хотя вру, еще есть вариант с bind. – Grundy Feb 17 '17 at 18:47
  • @Grundy, это же тот самый ДенисИванов, который что-то знает про call/apply. – vp_arth Feb 17 '17 at 18:51
  • @vp_arth, я думаю здесь просто путаница с вопросами при назначении конкурса, и конкурс должен был быть на этот вопрос – Grundy Feb 17 '17 at 18:58
  • Странно, назначал именно сюда. А переместилось туда ... – Денис Иванов Feb 17 '17 at 19:02
  • Этот вопрос был задан 2ч назад. О каком конкурсе речь? – vp_arth Feb 17 '17 at 19:12
  • @Grundy, не совсем понял. Я не работаю с JQuery, поэтому понять что такое $.when не могу – Денис Иванов Feb 17 '17 at 19:45
  • @ДенисИванов, именно поэтому в комментарии есть ссылка на документацию для этого метода:) – Grundy Feb 17 '17 at 19:47
  • @ДенисИванов привет, я модератор. Вы случайно назначили конкурс на другом вопросе и хотите его отменить? – Nick Volynkin Feb 18 '17 at 03:46
  • @Nick Volynkin, было бы неплохо. Сюда назначать не надо, ибо я сам ответил на свой вопрос путём проб и ошибок. Дополнил свой пост. Как то так – Денис Иванов Feb 20 '17 at 20:48
  • @ДенисИванов, это ваше дополнение желательно оформить и перенести в ответ. – vp_arth Feb 20 '17 at 20:49
  • @ vp_arth, я так делал до этого, но его удалили, сказали что нельзя отвечать на свой вопрос=) – Денис Иванов Feb 20 '17 at 20:51
  • http://ru.stackoverflow.com/help/self-answer – vp_arth Feb 20 '17 at 20:52
  • @ДенисИванов сейчас уже поздно отменять, хотя бы потому, что после объявления конкурса появились новые ответы. Будет нечестно по отношению к их авторам. В целом отменять конкурс можно только в порядке исключения и сразу — например, когда случайно не туда нажали. – Nick Volynkin Feb 21 '17 at 05:38
  • @ДенисИванов, не могли сказать, что нельзя отвечать на свой вопрос, могли сказать в данном сообщении не содержится ответа на вопрос. – Grundy Feb 21 '17 at 08:08
2

Путём проб и ошибок, мне кажется я уловил тонкий смысл различия между call/apply и хотел бы им с Вами поделиться, может ещё будет такой(-ая) же как я. Итак, допустим, у нас есть простая заданная функция, которая возвращает сумму двух чисел:

function sumTwoNumb(a, b){
    return a + b;
}

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


Если мы вызовем её как: sumTwoNumb.call(null, [100,150]), то получится так, что первый параметр a получит аргументом весь массив [100,150], а второй параметр, b не получит ничего и станет, соответственно, undefined. И в итоге функция вернёт результат сложения [100,150]+undefined, а это равно 100,150undefined.


А если мы её вызовем так: sumTwoNumb.apply(null, [100,150]), то при входе в функцию первый параметр a получит нулевой элемент массива = 100, а второй параметр получит первый элемент массива = 150 и в итоге функция вернёт результат сложения 100 + 150, то есть верный.

Уважаемые профессионалы, если я не прав, поправьте. Надеюсь, кому-то пригодится.

Nick Volynkin
  • 34,094
  • 1
    Твой ответ ничем не отличается от уже присутствующих. И к нему вполне подходит твой же комментарий: А что это даёт? Ну допустим я опущу аргументы в функции, а вытащу их через arguments. Если call - то там будет объект с массивом, а если apply то просто массив ... – Grundy Feb 21 '17 at 06:26
  • 2
    @Grundy, если его формулировка ему самому понятнее, то не исключено, что она же будет понятнее для других новичков. Я(и, судя по голосам, сообщество) считаю - пусть будет –  Feb 21 '17 at 06:57
  • 2
    @Anon, у меня претензия не к формулировке, а к тому, что он сам не ответил на свой же комментарий. Таким образом непонятно, что именно нового добавил его ответ к существующим, а также, как именно он отвечает на его же вопросы, которые он задавал в комментариях. – Grundy Feb 21 '17 at 08:12
  • 1
    @Anon, может и так, но задать вопрос, почитать ответы, скопировать их в свой "ответ" практически полностью - это нехорошо. Впрочем, понял -- и то ладно. –  Feb 22 '17 at 18:16
  • @Other, в том и дело, что читая ваши ответы сейчас, когда я понимаю в чем разница, я вижу что многие тут писали верные вещи, но мне нужен был элементарный пример, который показал бы как именно распределяются элементы массива по аргументам в call и в apply. Ваш ответ был абсолютно верным, но слишком расплывчатым. Сейчас я вижу кристалл Вашей мысли и мне понятно что Вы имели ввиду, но тогда не смог понять. Как я и говорю, мне требовался самый элементарный пример, чтобы я мог понять механику распределения этих элементов по параметрам. – Денис Иванов Feb 23 '17 at 10:21
  • 1
    @ДенисИванов, а что по поводу ответа vp_arth, в котором был пример с вызовом, который даже можно было запустить и увидеть разницу? И ты так и не ответил на свой же вопрос, про доставание из arguments. – Grundy Feb 26 '17 at 16:54
  • Про arguments: если у нас уже есть функция, в которой нельзя изменить код, можно только ей что-то передать и она что-то вернёт. То-есть можно через arguments - но когда есть доступ к телу функции и её несложно переписать. А по поводу ответа, который написал vp_arth ... сейчас я её вижу, но тогда не понимал. Есть отличная фраза: отладка сложнее написания кода, и если вы пишите код настолько заумно насколько сможете - то априори не сможете его отлаживать. Этой сентенцией я имею ввиду то, что мне для понимания не хватало именно элементарнейшего примера, чтобы понять механизм. В общем как-то так. – Денис Иванов Feb 27 '17 at 09:05
1

Всё верно, call в первом аргументе возвращает массив, а apply распределяет элементы массива по аргументам, т. е. arr будет указывать на первый элемент Первый элемент.

  • А что это даёт? Ну допустим я опущу аргументы в функции, а вытащу их через arguments. Если call - то там будет объект с массивом, а если apply то просто массив ... – Денис Иванов Feb 17 '17 at 17:47
  • 1
    Только не возвращает, а принимает. –  Feb 21 '17 at 06:57