電話と申し込みの違いは何ですか?


3105

関数の使用callapply呼び出しの違いは何ですか?

var func = function() {
  alert('hello!');
};

func.apply();func.call();

前述の2つの方法の間にパフォーマンスの違いはありますか?いつからcallapplyまたはその逆に使用するのが最適ですか?


727
aargsの配列の適用と、argsのc列の呼び出しについて考えます。
ラリーバトル

176
@LarryBattleほぼ同じですが、aは配列に適用され、cはコンマを要求します(つまり、コンマ区切りの引数)。
Samih 2013

3
私はそれが愚かであることに同意します。面倒なのは、影響力のあるチャンプが重要なjs質問のリストに質問を追加したため、インタビュー中にこの質問がどういうわけか聞かれるということです。
リンゴ

6
あなたは一度仕事に応募し(1つの引数)、あなたは何度も[電話] 人に電話をかけます(いくつかの引数)。代替案:Call of Dutyゲームは[多すぎる?]多くあります。
Gras Double

1
「this」値に関係なく引数値のリストを使用して可変個関数を呼び出す場合は、ES6スプレッド演算子を使用しfn(...input)ます(入力が配列の場合など)。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Gajus

回答:


3649

違いは、配列としてapply関数を呼び出すことができることですargumentscallパラメータを明示的にリストする必要があります。有用なニーモニックは、" A用rrayおよびCのためのC有するoMMA。"

適用呼び出しについては、MDNのドキュメントを参照してください。

疑似構文:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

ES6ではspreadcall関数で使用する配列の可能性もあります。互換性についてはこちらをご覧ください。

サンプルコード:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator


25
追加すべきことの1つは、argsが数値配列([])でなければならないことです。連想配列({})は機能しません。
Kevin Schroeder

326
@KevinSchroeder:JavaScriptの用語で、[]呼ばれる配列{}と呼ばれているオブジェクト
Martijn、2013年

89
私はよく、どれが配列を受け取り、引数をリストすることを期待するかを忘れていました。メソッドが開始の最初の文字場合、私はそれを覚えているために使用される技術である、それはすなわち、配列取る pply配列
アジズpunjani

16
通常の関数呼び出しの代わりに@SAM 呼び出しを使用することは、関数呼び出しでこの値を変更する必要がある場合にのみ意味があります。例(関数の引数オブ​​ジェクトを配列に変換するもの):Array.prototype.slice.call(arguments)または[].slice.call(arguments)。配列に引数がある場合、たとえば(ほぼ)同じパラメーターを使用して別の関数を呼び出す関数などに、applyは意味があります。推奨事項funcname(arg1)必要な場合は通常の関数呼び出しを使用し、本当に必要な場合は呼び出しを保存して特別な場合に適用します。
いくつかの

4
@KunalSingh Both callapply2つのパラメーターを受け取ります。apply' and call`関数の最初の引数は所有者オブジェクトである必要があり、2番目のパラメーターはそれぞれ配列またはカンマ区切りのパラメーターになります。あなたは合格した場合nullundefined、最初の引数として、非strictモードでは、それらはすなわちグローバルオブジェクトに置き換えられますwindow
AJ Qarshi

229

K.スコットアレンはこの問題について素晴らしい記事を書いています。

基本的に、関数の引数の処理方法が異なります。

apply()メソッドはcall()と同じですが、apply()では2番目のパラメータとして配列が必要です。配列はターゲットメソッドの引数を表します。」

そう:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

42
apply()およびcall()の2番目のパラメーターはオプションで、必須ではありません。
怒ったキウイ

34
最初のパラメータも必要ありません。
Ikrom

@Ikromは、最初のパラメータには、必要ありませんcallが、ための要件 apply
iamcastelli

160

各関数をいつ使用するかという部分に答えるには、apply渡す引数の数がわからない場合、またはそれらがすでに配列または配列のようなオブジェクト(arguments独自の引数を転送するオブジェクトのように)にある場合に使用します。call引数を配列でラップする必要がないため、それ以外の場合に使用します。

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

(例のように)引数を渡さないときは、関数を呼び出すcallので、私は好みます。(存在しない)引数に関数を適用していることを意味します。apply

apply引数を(たとえばのf.apply(thisObject, [a, b, c])代わりにf.call(thisObject, a, b, c))使用してラップする場合を除いて、パフォーマンスに違いはありません。私はそれをテストしていないので、違いがあるかもしれませんが、それは非常にブラウザ固有です。それはその可能性がありますcallあなたは既に配列内の引数を持っていない場合は高速ですapply、あなたがしなければ高速です。


111

ここに良いニーモニックがあります。 pplyは使用していますrraysとAの lways 1つのまたは2つの引数を取ります。Cを使用する場合は、引数の数をCに数える必要があります。


2
そこに便利なニーモニック!。最初のパラメータも2番目のパラメータapplyも必要ないため、「1つまたは2つの引数」を「最大2つの引数」と変更します。私は1つが呼び出されます、なぜかかわらわからないapplyか、callパラメータなし。誰かのように見えますが、なぜここに見つけることを試みているstackoverflow.com/questions/15903782/...
dantheta

92

これは古いトピックですが、.callは.applyよりもわずかに高速であることを指摘したいと思います。理由は正確には言えません。

jsPerf、http: //jsperf.com/test-call-vs-apply/3を参照してください


[ UPDATE!]

Douglas Crockfordが2つの違いを簡単に述べていますが、これはパフォーマンスの違いを説明するのに役立ちます... http://youtu.be/ya4UHuXNygM?t=15m52s

Applyは引数の配列を取りますが、Callは0個以上の個別のパラメータを取ります!ああ!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)


これは、関数がパラメーター/配列をどのように処理するかによって異なります。配列を処理する必要がない場合は、時間がかかりませんか?
Eric Hodonsky、2012年

12
興味深いことに、配列がない場合でも、呼び出しははるかに高速です。jsperf.com/applyvscallvsfn2
Josh Mc

@JoshMcそれは非常にブラウザ固有です。IE 11では、適用が呼び出しの2倍の速度で実行されます。
Vincent McNabb 2013年

1
1.新しいアレイを作成することは、ガベージコレクターがある時点でそれをクリーンアップする必要があることを意味します。2.間接参照を使用して配列内の項目にアクセスすることは、変数(パラメーター)に直接アクセスするよりも効率が悪いです。(私はそれが「解析」によってkmathenyが意味したものであると信じています。これは実際にはかなり異なるものです。)しかし、どちらの引数もjsperfを説明しません。これは、エンジンの2つの関数の実装に関連している必要があります。たとえば、何も渡されない場合でも、空の配列が作成される可能性があります。
joeytwiddle 2013年

テストとビデオを共有していただきありがとうございます
Gary

76

Closure:Michael BolinによるThe Definitive Guideの抜粋に従います。少し長く見えるかもしれませんが、多くの洞察で飽和しています。「付録B.よく誤解されているJavaScriptの概念」から:


this関数が呼び出されたときの意味

フォームの関数を呼び出すときfoo.bar.baz()、オブジェクトfoo.barはレシーバーと呼ばれます。関数が呼び出されると、次の値として使用されるのはレシーバですthis

var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
  for (var i = 0; i < arguments.length; i++) {
    this.value += arguments[i];
  }
  return this.value;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

関数が呼び出されたときに明示的なレシーバーがない場合、グローバルオブジェクトがレシーバーになります。47ページの「goog.global」で説明されているように、JavaScriptがWebブラウザーで実行されるときのwindowはグローバルオブジェクトです。これはいくつかの驚くべき動作につながります:

var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN

同じ関数obj.addValuesf参照していても、レシーバーの値は呼び出しごとに異なるため、呼び出されたときの動作は異なります。このため、を参照する関数を呼び出すときは、が呼び出されたときにが正しい値になるthisようにすることが重要thisです。明確にするためthisに、関数本体で参照されていない場合、f(20)およびの動作はobj.addValues(20)同じになります。

関数はJavaScriptのファーストクラスオブジェクトであるため、独自のメソッドを持つことができます。すべての関数にはメソッドがcall()あり、関数を呼び出すときにapply()レシーバー(つまり、参照するオブジェクトthis)を再定義することができます。メソッドのシグネチャは次のとおりです。

/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;

なお、唯一の違いcall()apply()つまりcall()、一方、個々の引数として関数パラメータを受信するapply()単一のアレイとしてそれらを受信します。

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);

次の呼び出しは同等でfありobj.addValues、同じ関数を参照しています。

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

しかしながら、どちらcall()apply()それが指定されていない受信機の引数の代わりに独自の受信機の値を使用して、次の意志ではない作品。

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

値はthis缶がなることはありませんnullか、undefined関数が呼び出されたとき。場合null又はundefined受信機として供給されるcall()、またはapply()、グローバルオブジェクトではなく、受信機のための値として使用されます。したがって、前のコードにはvalue、グローバルオブジェクトに名前が付けられたプロパティを追加するのと同じ望ましくない副作用があります。

関数は、それが割り当てられている変数を認識していないものと考えると役に立ちます。これは、関数の定義時ではなく、関数が呼び出されたときに、この値がバインドされるという考えを強化するのに役立ちます。


抽出の終わり。


事実に注意してください、それadditionalValuesobj.addValues体内で参照されていません
Viktor Stolbin '27 / 10/27

私はあなたが質問に答えていたことを知っていますが、付け加えたいと思います:fを定義するときにbindを使用することができます。 var f = obj.addValues;になるvar f = obj.addValues.bind(obj) と、f(20)は、毎回callを使用したり、適用したりする必要なく機能します。
jhliberty 2017年

あなたはそれを書かなかったと思いますが、あなたは本のテキストと例を関連性があるものとして強調しました、そして私はとても感謝しています。彼らはとても役に立ちました。
Fralcon

34

あるオブジェクトが別のオブジェクトの機能を借用すると便利な場合があります。つまり、借用オブジェクトは、貸し出された機能を、あたかも独自のものであるかのように実行するだけです。

小さなコード例:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

これらのメソッドは、オブジェクトに一時的な機能を与えるのに非常に役立ちます。


1
console.logチェックアウトの見方を知りたい人へ:console.logとは何ですか?
Michel Ayres、2014

25

呼び出し、適用、バインドの別の例。CallとApplyの違いは明らかですが、Bindは次のように機能します。

  1. バインドは実行可能な関数のインスタンスを返します
  2. 最初のパラメータは「これ」です
  3. 2番目のパラメータは、カンマで区切られた引数のリストです(Callなど)。

}

function Person(name) {
    this.name = name; 
}
Person.prototype.getName = function(a,b) { 
     return this.name + " " + a + " " + b; 
}

var reader = new Person('John Smith');

reader.getName = function() {
   // Apply and Call executes the function and returns value

   // Also notice the different ways of extracting 'getName' prototype
   var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
   console.log("Apply: " + baseName);

   var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
   console.log("Call: " + baseName);

   // Bind returns function which can be invoked
   var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
   console.log("Bind: " + baseName());
}

reader.getName();
/* Output
Apply: John Smith is a boy
Call: John Smith is a boy
Bind: John Smith is a boy
*/

23

'valueForThis'引数が使用されている例を示します。

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

**詳細:http : //es5.github.io/#x15.4.4.7 *


20

Call()はコンマで区切られた引数を取ります。例:

.call(scope, arg1, arg2, arg3)

そして、apply()は引数の配列をとります、例:

.apply(scope, [arg1, arg2, arg3])

ここにいくつかの使用例があります:http : //blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/


`// call()===コンマ区切りの引数(arguments-list).call(this、args1、args2、args3、...)// apply()===引数の配列(array-items)。 apply(this、[arr0、arr1、arr2、...]) `
xgqfrms

19

Function.prototype.apply()のMDNドキュメントから:

apply()メソッドはthis、配列(または配列のようなオブジェクト)として指定された値と引数を使用して関数を呼び出します。

構文

fun.apply(thisArg, [argsArray])

Function.prototype.call()のMDNドキュメントから:

call()メソッドは、特定のthis値と引数を個別に指定して関数を呼び出します。

構文

fun.call(thisArg[, arg1[, arg2[, ...]]])

JavaScriptのFunction.applyとFunction.callから:

apply()メソッドはcall()と同じですが、apply()では2番目のパラメータとして配列が必要です。配列は、ターゲットメソッドの引数を表します。


コード例:

var doSomething = function() {
    var arr = [];
    for(i in arguments) {
        if(typeof this[arguments[i]] !== 'undefined') {
            arr.push(this[arguments[i]]);
        }
    }
    return arr;
}

var output = function(position, obj) {
    document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';
}

output(1, doSomething(
    'one',
    'two',
    'two',
    'one'
));

output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [
    'one',
    'two',
    'two',
    'one'
]));

output(3, doSomething.call({one : 'Steven', two : 'Jane'},
    'one',
    'two',
    'two',
    'one'
));

このフィドルも参照してください。



10

これは小さめの投稿です、私はこれについて書きました:

http://sizeableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"

ここに別のものがあります:blog.i-evaluation.com/2012/08/15/javascript-call-and-apply しかし、基本的には正しいです:.call(scope、arg1、arg2、arg3)
Mark Karwowski


6

呼び出し方法と適用方法を以下のように区別できます

CALL:引数付きの関数が個別に提供します。渡す引数がわかっている場合、または渡す引数がない場合は、callを使用できます。

APPLY:配列として提供された引数を使用して関数を呼び出します。関数に渡す引数の数がわからない場合は、applyを使用できます。

apply over呼び出しを使用する利点があります。引数の数を変更する必要はなく、渡される配列を変更するだけです。

性能に大きな違いはありません。しかし、配列はapplyメソッドで評価する必要があるため、callはapplyに比べて少し速いと言えます。


5

これらのtoメソッドの違いは、パラメーターを渡す方法です。

「配列の場合はA、コンマの場合はC」は便利なニーモニックです。


11
この回答は、他の回答ではまだ十分に提供されていないものを提供していますか?
Kyll

5

this関数の実行時に値を強制するために、呼び出しと適用の両方が使用されます。唯一の違いは、あるcallかかるn+11は、引数thisとします'n' argumentsapply2つの引数のみを受け取り、1つthisは引数配列です。

私が見利点applyを超えるがcall、我々は簡単に多くの努力なしに、他の関数への関数呼び出しを委任することができるということです。

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

hellosayHello使用する方法を簡単に委任できますapplyが、callこれを実現するのは非常に困難です。


4

同じことcallapply成し遂げても、少なくとも1か所は使えずcall、しか使えないところがあると思いますapply。これは、継承をサポートし、コンストラクターを呼び出したい場合です。

これは、他のクラスを拡張してクラスの作成もサポートするクラスを作成できる関数です。

function makeClass( properties ) {
    var ctor = properties['constructor'] || function(){}
    var Super = properties['extends'];
    var Class = function () {
                 // Here 'call' cannot work, only 'apply' can!!!
                 if(Super)
                    Super.apply(this,arguments);  
                 ctor.apply(this,arguments);
                }
     if(Super){
        Class.prototype = Object.create( Super.prototype );
        Class.prototype.constructor = Class;
     }
     Object.keys(properties).forEach( function(prop) {
           if(prop!=='constructor' && prop!=='extends')
            Class.prototype[prop] = properties[prop];
     });
   return Class; 
}

//Usage
var Car = makeClass({
             constructor: function(name){
                         this.name=name;
                        },
             yourName: function() {
                     return this.name;
                   }
          });
//We have a Car class now
 var carInstance=new Car('Fiat');
carInstance.youName();// ReturnsFiat

var SuperCar = makeClass({
               constructor: function(ignore,power){
                     this.power=power;
                  },
               extends:Car,
               yourPower: function() {
                    return this.power;
                  }
              });
//We have a SuperCar class now, which is subclass of Car
var superCar=new SuperCar('BMW xy',2.6);
superCar.yourName();//Returns BMW xy
superCar.yourPower();// Returns 2.6

選択された回答に記載されているように、電話はスプレッドオペレーターと一緒に機能すると思います。私が何かを逃していない限り。
jhliberty 2017年

4

概要:

どちらも call()apply()はにあるメソッドFunction.prototypeです。したがって、プロトタイプチェーンを介してすべての関数オブジェクトで使用できます。両方call()apply()の指定された値を持つ関数を実行することができますthis

主な違いは call()apply()あなたがそれに引数を渡す必要があります方法です。両方のではcall()apply()、あなたは、最初の引数としてあなたに値にしたいオブジェクトを渡しますthis。他の引数は次のように異なります。

  • ではcall()、あなたが通常の引数に配置する必要があります(第二引数から始まります)
  • ではapply()、あなたは、引数の配列を渡す必要があります。

例:

let obj = {
  val1: 5,
  val2: 10
}

const summation = function (val3, val4) {
  return  this.val1 + this.val2 + val3 + val4;
}

console.log(summation.apply(obj, [2 ,3]));
// first we assign we value of this in the first arg
// with apply we have to pass in an array


console.log(summation.call(obj, 2, 3));
// with call we can pass in each arg individually

これらの関数を使用する必要があるのはなぜですか?

this値は、JavaScriptでトリッキー時々することができます。関数が定義されているときではなく、関数が実行されたときthis決定される私たちの機能が正しいthisバインディングに依存している場合、この動作を使用call()apply()て強制することができます。例えば:

var name = 'unwantedGlobalName';

const obj =  {
  name: 'Willem',
  sayName () { console.log(this.name);}
}


let copiedMethod = obj.sayName;
// we store the function in the copiedmethod variable



copiedMethod();
// this is now window, unwantedGlobalName gets logged

copiedMethod.call(obj);
// we enforce this to be obj, Willem gets logged


4

主な違いは、callを使用すると、スコープを変更して引数を通常どおり渡すことができますが、applyを使用すると、引数を配列として使用して呼び出すことができます(配列として渡す)。しかし、コードで何をするかという点では、それらはかなり似ています。

この関数の構文は、apply()の構文とほとんど同じですが、根本的な違いは、call()が引数リストを受け入れるのに対し、apply()は引数の単一の配列を受け入れることです。

ご覧のとおり、大きな違いはありませんが、それでも、call()またはapply()を使用したい場合があります。たとえば、applyメソッドを使用して、MDNから配列の最小値と最大値を見つける以下のコードを見てください。

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

したがって、主な違いは、引数を渡す方法だけです。

呼び出し:

function.call(thisArg, arg1, arg2, ...);

適用:

function.apply(thisArg, [argsArray]);

2

これについて少し詳しく説明します。

これら2つの呼び出しはほぼ同等です。

func.call(context, ...args); // pass an array as list with spread operator

func.apply(context, args);   // is same as using apply

わずかな違いがあります:

  • spreadオペレータは...渡すことができますが、反復可能 args呼び出しにリストとして。
  • applyのみ受け付け配列のような引数を。

したがって、これらの呼び出しはお互いを補完します。我々は期待してどこに反復可能なcall我々が期待する作品は、配列のようなapply作品を。

そして、実際の配列のように反復可能配列のようなオブジェクトの場合、技術的にはそれらのいずれかを使用できますが、ほとんどのJavaScriptエンジンが内部で最適化するため、適用はおそらくより高速になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.