C'est mon approche de la création de appelable objets correctement référence à leurs membres de l'objet, et de maintenir le bon niveau de l'héritage,
sans vous embêter avec des prototypes.
Il vous suffit de:
class ExFunc extends Function {
constructor() {
super('...args', 'return this.__call__(...args)');
return this.bind(this);
}
// Example `__call__` method.
__call__(a, b, c) {
return [a, b, c];
}
}
Étendre cette classe et ajouter un __call__
méthode, plus bas...
Une explication dans le code et les commentaires:
// A Class that extends Function so we can create
// objects that also behave like functions, i.e. callable objects.
class ExFunc extends Function {
constructor() {
// Here we create a dynamic function with `super`,
// which calls the constructor of the parent class, `Function`.
// The dynamic function simply passes any calls onto
// an overridable object method which I named `__call__`.
// But there is a problem, the dynamic function created from
// the strings sent to `super` doesn't have any reference to `this`;
// our new object. There are in fact two `this` objects; the outer
// one being created by our class inside `constructor` and an inner
// one created by `super` for the dynamic function.
// So the reference to this in the text: `return this.__call__(...args)`
// does not refer to `this` inside `constructor`.
// So attempting:
// `obj = new ExFunc();`
// `obj();`
// Will throw an Error because __call__ doesn't exist to the dynamic function.
super('...args', 'return this.__call__(...args)');
// `bind` is the simple remedy to this reference problem.
// Because the outer `this` is also a function we can call `bind` on it
// and set a new inner `this` reference. So we bind the inner `this`
// of our dynamic function to point to the outer `this` of our object.
// Now our dynamic function can access all the members of our new object.
// So attempting:
// `obj = new Exfunc();`
// `obj();`
// Will work.
// We return the value returned by `bind`, which is our `this` callable object,
// wrapped in a transparent "exotic" function object with its `this` context
// bound to our new instance (outer `this`).
// The workings of `bind` are further explained elsewhere in this post.
return this.bind(this);
}
// An example property to demonstrate member access.
get venture() {
return 'Hank';
}
// Override this method in subclasses of ExFunc to take whatever arguments
// you want and perform whatever logic you like. It will be called whenever
// you use the obj as a function.
__call__(a, b, c) {
return [this.venture, a, b, c];
}
}
// A subclass of ExFunc with an overridden __call__ method.
class DaFunc extends ExFunc {
get venture() {
return 'Dean';
}
__call__(ans) {
return [this.venture, ans];
}
}
// Create objects from ExFunc and its subclass.
var callable1 = new ExFunc();
var callable2 = new DaFunc();
// Inheritance is correctly maintained.
console.log('\nInheritance maintained:');
console.log(callable2 instanceof Function); // true
console.log(callable2 instanceof ExFunc); // true
console.log(callable2 instanceof DaFunc); // true
// Test ExFunc and its subclass objects by calling them like functions.
console.log('\nCallable objects:');
console.log( callable1(1, 2, 3) ); // [ 'Hank', 1, 2, 3 ]
console.log( callable2(42) ); // [ 'Dean', 42 ]
Vue sur repl.il
D'autres explications bind
:
function.bind()
fonctionne comme l' function.call()
, et ils partagent une même signature de la méthode:
fn.call(this, arg1, arg2, arg3, ...);
plus sur mdn
fn.bind(this, arg1, arg2, arg3, ...);
plus sur mdn
Dans le premier argument redéfinit l' this
contexte à l'intérieur de la fonction. Des arguments supplémentaires peuvent également être lié à une valeur.
Mais où call
immédiatement les appels de la fonction avec les valeurs soumises, bind
renvoie un "exotique" de la fonction de l'objet que de manière transparente encapsule l'original, this
et les arguments de préréglage.
Ainsi, lorsque vous définissez une fonction d' bind
certains de ses arguments:
var foo = function(a, b) {
console.log(this);
return a * b;
}
foo = foo.bind(['hello'], 2);
Vous appelez la limite de la fonction avec seulement les arguments restants, son contexte est prédéfini, dans ce cas - ['hello']
.
// We pass in arg `b` only because arg `a` is already set.
foo(2); // returns 4, logs `['hello']`