2011-09-24 17 views
47
// Don't break the function prototype. 
// pd - https://github.com/Raynos/pd 
var proto = Object.create(Function.prototype, pd({ 
    "prop": 42 
})); 

var f = function() { return "is a function"; }; 
f.__proto__ = proto; 

console.log(f.hasOwnProperty("prop")); // false 
console.log(f.prop); // 42 
console.log(f()); // "is a function" 

.__proto__ non standard e obsoleto.Come ereditare le funzioni javascript?

Come dovrei ereditare la creazione prototipica di un oggetto ma avere quell'oggetto essere una funzione.

Object.create restituisce un oggetto non una funzione.

new Constructor restituisce un oggetto non una funzione.

Motivazione: - Un cross-browser finherit

var finherit = function (parent, child) { 
    var f = function() { 
     parent.apply(this, arguments); 
     child.apply(this, arguments); 
    }; 
    f.__proto__ = parent; 
    Object.keys(child).forEach(function _copy(key) { 
     f[key] = child[key]; 
    }); 
    return f; 
}; 

io non credo che questo è possibile, in modo probabilmente dovremmo proporre una Function.create ai es-discuss mailing list

/* 
    Creates a new function whose prototype is proto. 
    The function body is the same as the function fbody. 
    The hash of propertydescriptors props is passed to defineproperties just like 
    Object.create does. 
*/ 
Function.create = (function() { 
    var functionBody = function _getFunctionBody(f) { 
    return f.toString().replace(/.+\{/, "").replace(/\}$/, ""); 
    }; 
    var letters = "abcdefghijklmnopqrstuvwxyz".split(""); 

    return function _create(proto, fbody, props) { 
    var parameters = letters.slice(0, fbody.length); 
    parameters.push(functionBody(fbody)); 
    var f = Function.apply(this, parameters); 
    f.__proto__ = proto; 
    Object.defineProperties(f, props); 
    return f; 
    }; 
})(); 

Related es-discuss mail

Come indicato nel thread es-discuss esiste a ES: strawman <| operatore prototipo che consentirebbe questo.

Vediamo cosa sarebbe simile utilizzando <|

var f1 = function() { 
    console.log("do things"); 
}; 

f1.method = function() { return 42; }; 

var f2 = f1 <| function() { 
    super(); 
    console.log("do more things"); 
} 
console.log(f1.isPrototypeOf(f2)); // true 
console.log(f2()); // do things do more things 
console.log(f2.hasOwnProperty("method")); // false 
console.log(f2.method()); // 42 
+0

'console.log (f.apply);' restituirà 'undefined' ... in modo che il modo non sia ottimale in alcun modo ... continuando a pensare ad un'alternativa. –

+0

@FelixKling Ho dimenticato che dovrei ereditare da 'Function.prototype' grazie – Raynos

+0

C'era [questa domanda] (http://stackoverflow.com/questions/6564481/javascript-polymorphic-callable-objects) Ho incontrato un po 'di tempo fa il richiedente indicato [questo articolo] (http://ajaxian.com/archives/javascript-tips-for-rookies-and-gurus) che mostrava un modello per la creazione di * "oggetti calorici polimorfi" *. Non sono sicuro se è quello che stai cercando, ma potrebbe valere la pena dare un'occhiata. – user113716

risposta

9

Spero che ho la comprensione di questo diritto.

Credo che tu voglia un functor che sia sia un'istanza di un prototipo predefinito (sì, una classe, solo non una classe classica) sia direttamente richiamabile? Destra? Se è così, questo ha perfettamente senso ed è molto potente e flessibile (specialmente in un ambiente altamente asincrono come JavaScript). Purtroppo non c'è modo di farlo elegantemente in JavaScript senza manipolare __proto__. Puoi farlo definendo una funzione anonima e copiando tutti i riferimenti a tutti i metodi (che sembra essere la direzione che stavi dirigendo) per fungere da classe proxy. Gli svantaggi di questo sono ...

  1. È molto costoso in termini di runtime.
  2. (functorObj instanceof MyClass) non sarà mai true.
  3. Le proprietà non saranno direttamente accessibili (se fossero tutte assegnate per riferimento, questa sarebbe una storia diversa, ma le primitive vengono assegnate in base al valore). Questo può essere risolto con gli accessor tramite defineProperty o semplicemente con i metodi di accesso nominati se necessario (sembra che sia quello che stai cercando, basta aggiungere tutte le proprietà al funtore con defineProperty tramite getter/setter invece di solo funzioni se non lo fai bisogno di supporto cross-engine/retrocompatibilità).
  4. È probabile che si verifichino casi limite in cui i prototipi nativi finali (come Object.prototype o Array.prototype [se si eredita ciò]] potrebbero non funzionare come previsto.
  5. Calling functorObj(someArg) sarà sempre rendono contesto this essere l'oggetto, a prescindere se si chiama functorObj.call(someOtherObj, someArg) (questo non è il caso per il metodo chiama però)
  6. Poiché l'oggetto funtore si crea al momento della richiesta, sarà bloccato nel tempo e la manipolazione del prototipo iniziale non influirà sugli oggetti del funtore assegnato come se fosse interessato da un oggetto normale (la modifica di MyClass.prototype non avrà alcun effetto sugli oggetti del funtore e anche il contrario è vero).

Se lo usi delicatamente, niente di tutto questo dovrebbe essere un grosso problema.

Nel prototipo della classe definire qualcosa di simile ...

// This is you're emulated "overloaded" call() operator. 
MyClass.prototype.execute = function() { 
    alert('I have been called like a function but have (semi-)proper access to this!'); 
}; 

MyClass.prototype.asFunctor = function(/* templateFunction */) { 
    if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function')) 
     throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?'); 
    // This is both the resulting functor proxy object as well as the proxy call function 
    var res = function() { 
     var ret; 
     if (res.templateFunction !== null) 
     // the this context here could be res.asObject, or res, or whatever your goal is here 
     ret = res.templateFunction.call(this, arguments); 
     if (typeof res.asObject.execute === 'function') 
     ret = res.asObject.execute.apply(res.asObject, arguments); 
     return ret; 
    }; 
    res.asObject = this; 
    res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null; 
    for (var k in this) { 
     if (typeof this[k] === 'function') { 
     res[k] = (function(reference) { 
      var m = function() { 
       return m.proxyReference.apply((this === res) ? res.asObject : this, arguments); 
      }; 
      m.proxyReference = reference; 
      return m; 
     })(this.asObject[k]); 
     } 
    } 
    return res; 
}; 

Con conseguente utilizzo sarebbe simile ...

var aobj = new MyClass(); 
var afunctor = aobj.asFunctor(); 
aobj.someMethodOfMine(); // << works 
afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context). 
afunctor('hello'); // << works by calling aobj.execute('hello'); 

(aobj instanceof MyClass) // << true 
(afunctor instanceof MyClass) // << false 
(afunctor.asObject === aobj) // << true 

// to bind with a previous function... 
var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); }); 
afunctor() // << first calls the original, then execute(); 
// To simply wrap a previous function, don't define execute() in the prototype. 

Si potrebbe anche legarsi catena innumerevoli altri oggetti/funzioni/ecc. fino a quando le mucche tornarono a casa. Basta refactoring la chiamata proxy un po '.

Spero che questo aiuti. Oh, e naturalmente è possibile cambiare il flusso di fabbrica in modo che un costruttore chiamato senza l'operatore new instanzia quindi un nuovo oggetto e restituisca l'oggetto functor. Comunque preferisci (potresti sicuramente farlo anche in altri modi).

Infine, per fare in modo che qualcuna funzione diventi l'operatore di esecuzione per un funtore in un modo un po 'più elegante, basta rendere il proxy un metodo di Function.prototype e passarlo all'oggetto da avvolgere se si vuole fare qualcosa di simile (si dovrebbe scambiare templateFunction con this e this con l'argomento ovviamente) ...

var functor = (function() { /* something */ }).asFunctor(aobj); 
+3

Fresco. Non avevo mai sentito parlare di funtori. Definirò sicuramente il mio prossimo funtore di cani. – Ben

+0

Questo è veramente uno pseudo-functor (sì, un nome divertente) che trasmette tutto. È un modo sciatto per farlo accadere. Alcune lingue supportano questo in modo nativo. Se EMCAScript5 consentiva la manipolazione di '__proto__' (o uno standard alternativo equivalente), ** o ** overloads dell'operatore, anche JavaScript. Purtroppo no. Per tutti è potere e flessibilità ... sospiro. –

+0

@ mr.stobbe potresti provare a usare i proxy ES6 per farlo? (Funziona in chrome e FF). In alternativa puoi imbrogliare e fingere che esista '<|' ('protoOf = function (proto, thing) {thing .__ proto__ = proto;}') – Raynos

Problemi correlati