powerman
@powerman
Systems Architect, Senior Go/Perl Linux Developer

JavaScript: что делает Function.call.apply(…)?

В JavaScript Garden в разделе «Functions / The arguments Object» есть вот такая строчка:

// Result: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);


Знатоки JS, поясните плз, в чём смысл записи Function.call.apply? Насколько я понимаю, и call и apply это обычные методы объекта Function, и вызов Function.call.apply это то же самое что вызов Function.apply. В крайнем случае, если по какой-то причине здесь важно вызвать .apply от нового пустого объекта типа Function возвращённого вызовом call, тогда получается что можно было с тем же успехом сделать и Function.apply.apply?
  • Вопрос задан
  • 12403 просмотра
Решения вопроса 1
mnasyrov
@mnasyrov
Всё просто :). Цель этого трюка — написать обёртку foo(), которая вызывает bar() в указанном контексте context и с переданными аргументами arg1, arg2 без лишних теложвижений.

В традиционном виде, это выглядело бы так

function bar(arg1, arg2) {}

function foo(context, arg1, arg2) {
bar.call(context, arg1, arg2);
}


Или так для произвольного числа аргументов:

function bar() {}

function foo() {
var context = arguments[0];
var args = Array.prototype.slice.call(arguments, 1); //делаем массив аргументов для bar
bar.apply(context, args);
}


Вместо этого салата использовать трюк с call.apply:

function foo() {
Function.call.apply(bar, arguments);
}


Работает это так: aplly вызывает Function.call на объекте bar c переданными в foo параметрами. То есть получаем следующее для самого первого примера с context и arg1, arg2:

bar.call(context, arg1, arg2)

Что нам и нужно.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
taliban
@taliban
php программист
call изменяет this внутри вызываемой функции.
пример
var obj = {};
Array.push.call( obj, 1 );

Вызовется метод push у обьекта Array но внутри этого метода this будет указывать на обьект который я указал первым параметром, тоесть мой obj;

Зачем он и как его использовать?
Например, функции setTimeout и setInterval когда вызывают метод, то они вызывают этот метод без привязки к родителю, соответственно в вызывамых методах будет this = window
С помошью call это можно изменить и насильно указать что хочется. Это как простейший пример
Ответ написан
azproduction
@azproduction
В примере они сделали так, чтобы не отделять первый элемент массива — сделать красиво.

В свое время сломал мозг когда разбирал, вот примеры, которые все объясняет:
Function.prototype.call.apply(function(a,b){console.log([this,a,b])}, [NaN,{},4], {}, 0);
(function(a,b){console.log([this,a,b])}).apply(NaN, [{},4], {}, 0);
// Оба выдают [NaN {}, Object {}, 4]
// NaN будет объектом т.к. был передан в качестве первого параметра

Function.prototype.call.call(function(a,b){console.log([this,a,b])}, [NaN,{},4], {}, 0);
(function(a,b){console.log([this,a,b])}).call([NaN,{},4], {}, 0);
// Оба выдают [[NaN, Object {}, 4], Object {}, 0]
// Массив [NaN,{},4] уже объект поэтому не преобразуется в объект
Ответ написан
gaelpa
@gaelpa
Зачем: для создания метода объекта, принимающего подстановку «this» как первый параметр.
Function.apply.apply даст другой эффект ввиду того, что apply и call принимают разные параметры.

Зачем на более высоком уровне: приходит в голову только как вариант для построения библиотеки утилит для организации «красивого» вызова и незамусоривания пространства имён

При вызове происходит следующее, если кому интересно:
// в контексте примера
var a={ toString:function(){return 'a'} };
Foo.method(a,1,2,3);

1. в apply передается: объект «Foo.prototype.method», параметры [a,1,2,3]
2. вызывается Foo.prototype.method.call(a, 1,2,3), т.е. Foo.prototype.method от имени объекта a
3. вызывается console.log(a, 1,2,3); т.к. this===a

P.S. исходный пример целиком:
Another trick is to use both call and apply together to create fast, unbound wrappers.
function Foo() {}

Foo.prototype.method = function(a, b, c) {
    console.log(this, a, b, c);
};

// Create an unbound version of "method" 
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {
    // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
    Function.call.apply(Foo.prototype.method, arguments);
};
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы