2016-08-20 2 views
-1

Ich lerne funktionale Programmierung und node.js, und ich stieß auf dieses seltsame Problem bei der Verwendung von Function.prototype.apply und .bind. Sind Funktionsargumente nicht unbedingt Objekte?

function Spy(target, method) { 
    var obj = {count: 0}; 
    var original = target[method] 
    target[method] = function(){//no specified arguments 
     obj.count++ 
     original.apply(this, arguments)//only arguments property passed 
    } 
    return obj; 
} 
module.exports = Spy 

Dieser Code funktioniert, es Spione erfolgreich auf target.method.


//same code here 
    target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 
//same code here 

Dieser Code funktioniert jedoch nicht. Es gibt eine Fehlermeldung: TypeError: CreateListFromArrayLike called on non-object.


Und dann ist die größte Überraschung, diese Methode funktioniert einwandfrei.

//same code here 
    target[method] = function (args){ 
     obj.count++ 
     original.bind(this, args) 
    } 
//same code here 

Also warum genau bekomme ich diesen Fehler? Liegt es daran, dass Funktionsargumente nicht notwendigerweise Objekte sind? Oder ist es, weil anwenden eine strengere Beschreibung als binden?

+3

Nun ja, ['apply'] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) und [' bind'] (https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) haben sehr unterschiedliche Beschreibungen. 'apply' nimmt als zweiten Parameter ein Array oder ein [' arguments' Objekt] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) - wenn Sie nicht ' t an einem vorbei, klagt es. – Bergi

+0

Sehr unklar. Erstens, das funktioniert/funktioniert nicht Code-Fragmente sind identisch (es sei denn, ich vermisse etwas). Zweitens ruft 'bind' die Funktion nicht auf, daher ist es schwer zu glauben, dass" funktioniert ". – Amit

+0

@Amit tut mir leid, ich werde bearbeiten, damit es mehr offensichtlich ist. –

Antwort

1

In dieser Version:

target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 

Hier werden Sie nicht alle Argumente nehmen, sondern nur eine, namens args. Da ein Array-ähnliches Objekt erwartet, können Sie args nicht verwenden, da es nur das erste Argument ist, das an das ursprüngliche Ziel übergeben wurde.

können Sie ändern Sie es an:

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.apply(this,[arg]) //one argument passed here 
} 

Jetzt funktioniert es, aber Sie können auf ein Argument funktioniert nur auszuspionieren. Mit call wäre besser, da Sie nur ein zusätzliches Argument haben:

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.call(this,arg) //one argument passed here 
} 

Jetzt bind ist ein ganz anderes Tier. Es partial applies Funktionen, also Rückkehrfunktionen. Stellen Sie sich vor, Sie müssen einen Rückruf senden, der keine Argumente annimmt, aber eine Funktion mit einigen Argumenten aufruft, die Sie beim Erstellen haben. Sie sehen Code wie:

var self = this; 
return function() { 
    self.method(a, b); 
} 

Nun. bind tut dies für Sie:

return this.method.bind(this, a, b); 

Wenn eine dieser zurück Funktionen aufrufen geschieht das gleiche. Die Methode method wird mit den Argumenten a und b aufgerufen.Wenn Sie also bind für eine Funktion aufrufen, wird eine teilweise angewendete Version dieser Funktion zurückgegeben und nicht wie call oder apply aufgerufen.

+0

Bind nicht curry. Wenn überhaupt, ist es teilweise Anwendung, aber nicht einmal das ist sein Hauptzweck. – Bergi

+0

@Bergi Du hast Recht Teilapplikation ist ein besserer Begriff für eifrige Sprachen, aber ich denke, du liegst falsch mit dem "Hauptzweck" von 'bind', da es genau das ist, was es in einem" Ruf "-Kontext tut. – Sylwester

1

bind heißt genauso wie call ist, obwohl sie sehr unterschiedliche Dinge tun.

Wenn Sie wirklich auf diese Weise bind verwenden wollten. Sie könnten die Ausbreitung Operator (ES2015) verwenden, um die Argumente ‚Array‘ auf einzelne Argumente zu erweitern:

original.bind(null, ...args); 

dass die original Funktion mit den Array-Werte als einzelne Argumente binden.

+1

Ich habe keine Ahnung, warum Sie Scheibe verwenden, wenn Sie bereits ausgebreitet Operator haben. Nasse Affen. – Sylwester

+0

@Sylwester Das ist wahr, ich habe völlig vergessen, dass Sie den Spread-Operator auch für Arrays und Array-ähnliche Objekte verwenden können. Die Konvertierung vor der Verwendung wurde entfernt. – Snivels