2012-05-15 11 views
10

Ero alla ricerca di soluzioni per chiamare i costruttori Javascript con un numero arbitrario di argomenti e ho trovato alcuni buoni post SO, che mi hanno portato a credere che queste tre chiamate dovessero funzionare allo stesso modo. Tuttavia, almeno in Rhino e node.js, non lo fanno:associazione/applicazione di costruttori in JavaScript

1. f = Date.bind(Date, 2000,0,1) 
2. g = Date.bind.call(Date, 2000, 0, 1) 
3. h = Date.bind.apply(Date, [2000, 0, 1]) 

Il primo ha il risultato desiderato:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 

Ma gli altri due no:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST) 
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST) 

Quindi qualcosa è andato in tilt da qualche parte. Pensieri su cosa? È una cattiva idea mescolare cose come apply, bind e/o call con new?

+0

Tutti falliscono in IE 8 perché non esiste 'Date.bind'. ;-) – RobG

+0

Mi sembra che si tratti di un uso inappropriato di "bind". Dovrebbe essere usato per creare oggetti funzionali, ma le date sono oggetti Date, non funzioni e non possono essere chiamati. Inoltre, senza l'uso di bind o call o apply, il valore * this * è * Date * comunque, quindi qual è il punto? Infine, non si sta chiamando Date come costruttore ma come funzione. – RobG

+0

Certo, le date sono oggetti Data, ma la Data stessa è una funzione, ed è la Data stessa a cui sto tentando di collegarmi. La data è una funzione, quindi la lego e ottengo una nuova funzione, che poi uso come costruttore chiamandola con 'new'. Come ho detto all'inizio della domanda, il punto effettivo è di poter chiamare un costruttore con un elenco di argomenti determinati in fase di esecuzione. –

risposta

23

Il previously accepted answer non era corretto. Puoi usare bind, chiamare e applicare con i costruttori per creare nuovi costruttori, l'unico problema nel tuo test è che hai dimenticato che bind.apply e bind.call stanno applicando e chiamando bind, non il costruttore stesso , quindi hai dato gli argomenti sbagliati.

f = Date.bind(null, 2000,0,1) 
g = Function.bind.call(Date, null, 2000, 0, 1) 
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ]) 

new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST) 

Tutti e tre sono instanceof Data.

Gli argomenti della chiamata sono il contesto di esecuzione seguito dagli argomenti da applicare. Gli argomenti di Apply sono il contesto di esecuzione e un array di argomenti. Gli argomenti di bind sono il contesto di esecuzione seguito dagli argomenti da associare.

Così gli argomenti da applicare, per esempio, sono il contesto per cui applicarelegano (Data), seguita da una matrice che è gli argomenti per bind (in modo che il primo membro array è contestuale del bind discussione). Questo è il motivo per cui è complicato chiamare o applicare il binding; è strano fornire argomenti di contesto a entrambi.

Si noti che, quando si utilizza il bind con i costruttori, l'argomento di contesto viene sempre ignorato perché "nuovo" crea esplicitamente un nuovo contesto. Io uso null quando l'argomento di contesto è irrilevante per mantenerlo chiaro, ma può essere qualsiasi cosa.

Nel frattempo, applicare e chiamare in questi esempi è necessario sapere che il contesto in cui devono essere applicati/chiamare bind è la funzione Date. Ho spostato "Data" su "Funzione", ove possibile, per aiutare a illuminare ciò che effettivamente fornisce il contesto in cui. Quando chiamiamo apply o call su Date.bind, stiamo davvero chiamando apply o call sul metodo bind non collegato all'oggetto Date. Il metodo di binding in questo caso potrebbe provenire da qualsiasi funzione. Potrebbe essere Number.bind.call (Date, null, 2000, 0, 1) e il risultato sarebbe esattamente lo stesso.

Se non è ovvio perché, considerare la differenza tra i seguenti esempi:

context.method(); 

e

var noLongerAMethod = context.method; 
noLongerAMethod(); 

Nel secondo caso, il metodo è stato separato dal suo contesto originario (.. .non è stato precedentemente associato) e si comporterà diversamente se si basasse su "questo" internamente. Quando eseguiamo il binding di una determinata funzione come proprietà, anziché eseguirla direttamente, è semplicemente un altro puntatore al metodo di bind generico su Function.prototype.

Personalmente non penso di aver mai avuto bisogno di chiamare o applicare binding, ed è difficile immaginare una situazione per cui sarebbe una buona soluzione, ma i costruttori vincolanti per creare nuovi costruttori sono qualcosa che ho trovato molto utile a volte. In ogni caso è un puzzle divertente.

+0

Questa è stata una risposta molto utile. Grazie! –

5

bind e apply/call funziona solo lavoro invocazione a funzione ma non costruttore, in modo sostanzialmente con metodi nativi non si può fare questo, in un modo è quello di scrivere un metodo bindConstruct ma può coinvolge ulteriore complessità:

function bindConstruct(fn) { 
    // since constructor always accepts a static this value 
    // so bindConstruct cannot specify this 
    var extraArgs = [].slice.call(arguments, 1); 

    // create a 'subclass' of fn 
    function sub() { 
     var args = extraArgs.concat([].slice.call(arguments)); 
     fn.apply(this, args); 
    } 
    sub.prototype = fn.prototype; 
    sub.prototype.constructor = sub; 

    return sub; 
} 

Questo, in realtà, crea una sottoclasse nel costruttore.

Poi il tuo codice:

var MyClass = function(x, y) { 
    console.log(arguments); 
    console.log(x + y); 
} 
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3); 
var c = new BindedMyClass(4, 5); 
console.log(c instanceof MyClass); 
console.log(c instanceof BindedMyClass); 

Si può anche scrivere questa funzione per Function.prototype o come un'estensione della funzione nativa bind.

+1

Grazie, ma purtroppo, anche questo non funziona correttamente. I metodi Date non funzionano su alcun oggetto che creo con la mia nuova funzione "Date subclass" con "TypeError: Method called on incompatible object". Dimostrando ancora una volta che Javascript non ha realmente sottoclassi. Oppure le classi, del resto ... –

+0

"bind e applica/chiama solo il richiamo del lavoro di lavoro alla funzione ma non il costruttore" non è vero; vedi sotto. – Semicolon