Come stavi chiedendo una questione accademica, suppongo compatibilità del browser non è un problema. Se davvero non lo è, mi piacerebbe introdurre i proxy dell'armonia per questo. onerror
non è una buona pratica in quanto è solo un evento sollevato se da qualche parte si verifica un errore. Dovrebbe, se mai, essere utilizzato solo come ultima risorsa. (So che hai detto che non lo usi comunque, ma lo onerror
non è molto adatto per gli sviluppatori.)
In pratica, i proxy consentono di intercettare la maggior parte delle operazioni fondamentali in JavaScript, in particolare ottenendo qualsiasi proprietà che sia utile qui. In questo caso, è possibile intercettare la procedura per ottenere .slice
.
Nota che i proxy sono "buchi neri" per impostazione predefinita. Non corrispondono a nessun oggetto (ad esempio, l'impostazione di una proprietà su un proxy chiama semplicemente la trappola set
(interceptor), la memorizzazione effettiva che devi fare tu stesso). Ma esiste un "forwarding handler" che indirizza tutto attraverso un oggetto normale (o un'istanza ovviamente), in modo che il proxy si comporti come un oggetto normale. Estendendo il gestore (in questo caso, la parte get
), è possibile instradare facilmente i metodi Array.prototype
nel modo seguente.
Così, ogni volta qualsiasi proprietà (con il nome name
) è di essere recuperato, il percorso di codice è il seguente:
- Prova ritorno
inst[name]
.
- Altrimenti, provare a restituire una funzione che applica
Array.prototype[name]
all'istanza con gli argomenti specificati a questa funzione.
- In caso contrario, è sufficiente restituire
undefined
.
Se si vuole giocare con i proxy, è possibile utilizzare una versione recente di V8, ad esempio in una nightly build di Chromium (assicurarsi per l'esecuzione come chrome --js-flags="--harmony"
). Di nuovo, i proxy non sono disponibili per l'utilizzo "normale" perché sono relativamente nuovi, cambiano molte parti fondamentali di JavaScript e in realtà non sono ancora stati specificati ufficialmente (ancora bozze).
Questo è un semplice diagramma di come va (inst
è in realtà il proxy in cui è stata inserita l'istanza). Si noti che illustra solo ottenendo una proprietà; tutte le altre operazioni vengono semplicemente passate dal proxy a causa del gestore di inoltro non modificato.

codice La delega potrebbe essere la seguente:
function Test(a, b, c) {
this[0] = a;
this[1] = b;
this[2] = c;
this.length = 3; // needed for .slice to work
}
Test.prototype.foo = "bar";
Test = (function(old) { // replace function with another function
// that returns an interceptor proxy instead
// of the actual instance
return function() {
var bind = Function.prototype.bind,
slice = Array.prototype.slice,
args = slice.call(arguments),
// to pass all arguments along with a new call:
inst = new(bind.apply(old, [null].concat(args))),
// ^is ignored because of `new`
// which forces `this`
handler = new Proxy.Handler(inst); // create a forwarding handler
// for the instance
handler.get = function(receiver, name) { // overwrite `get` handler
if(name in inst) { // just return a property on the instance
return inst[name];
}
if(name in Array.prototype) { // otherwise try returning a function
// that calls the appropriate method
// on the instance
return function() {
return Array.prototype[name].apply(inst, arguments);
};
}
};
return Proxy.create(handler, Test.prototype);
};
})(Test);
var test = new Test(123, 456, 789),
sliced = test.slice(1);
console.log(sliced); // [456, 789]
console.log("2" in test); // true
console.log("2" in sliced); // false
console.log(test instanceof Test); // true
// (due to second argument to Proxy.create)
console.log(test.foo); // "bar"
Il gestore di inoltro è disponibile presso the official harmony wiki.
Proxy.Handler = function(target) {
this.target = target;
};
Proxy.Handler.prototype = {
// Object.getOwnPropertyDescriptor(proxy, name) -> pd | undefined
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getPropertyDescriptor(proxy, name) -> pd | undefined
getPropertyDescriptor: function(name) {
var desc = Object.getPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getOwnPropertyNames(proxy) -> [ string ]
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(this.target);
},
// Object.getPropertyNames(proxy) -> [ string ]
getPropertyNames: function() {
return Object.getPropertyNames(this.target);
},
// Object.defineProperty(proxy, name, pd) -> undefined
defineProperty: function(name, desc) {
return Object.defineProperty(this.target, name, desc);
},
// delete proxy[name] -> boolean
delete: function(name) { return delete this.target[name]; },
// Object.{freeze|seal|preventExtensions}(proxy) -> proxy
fix: function() {
// As long as target is not frozen, the proxy won't allow itself to be fixed
if (!Object.isFrozen(this.target)) {
return undefined;
}
var props = {};
Object.getOwnPropertyNames(this.target).forEach(function(name) {
props[name] = Object.getOwnPropertyDescriptor(this.target, name);
}.bind(this));
return props;
},
// == derived traps ==
// name in proxy -> boolean
has: function(name) { return name in this.target; },
// ({}).hasOwnProperty.call(proxy, name) -> boolean
hasOwn: function(name) { return ({}).hasOwnProperty.call(this.target, name); },
// proxy[name] -> any
get: function(receiver, name) { return this.target[name]; },
// proxy[name] = value
set: function(receiver, name, value) {
this.target[name] = value;
return true;
},
// for (var name in proxy) { ... }
enumerate: function() {
var result = [];
for (var name in this.target) { result.push(name); };
return result;
},
// Object.keys(proxy) -> [ string ]
keys: function() { return Object.keys(this.target); }
};
suona come stai pensando di javascript [ "pollyfills" o "shim"] (http://remysharp.com/2010/10/08/what-is-a-polyfill/) –
per inciso di solito è più elegante e affidabile catturare le eccezioni con un blocco try-catch. –