Как вы спрашивали академический вопрос, я полагаю, совместимость браузера не является вопрос. Если это действительно так, я бы хотел представить для этого прокси-серверы гармонии. onerror
не очень хорошая практика, поскольку это только событие, поднятое, если где-то произошла ошибка. Он должен, если вообще когда-либо, использоваться только в качестве крайней меры. (Я знаю, что вы сказали, что не используете его в любом случае, но onerror
просто не очень подходит для разработчиков.)
В основном, прокси-серверы позволяют перехватить большинство основных операций в JavaScript - прежде всего, получить какое-либо свойство, которое полезно здесь. В этом случае вы можете перехватить процесс получения .slice
.
Обратите внимание, что прокси-серверы являются «черными дырами» по умолчанию. Они не соответствуют ни одному объекту (например, установка свойства в прокси-сервере просто вызывает ловушку (перехватчик) set
, фактическое сохранение, которое вы должны сделать сами). Но есть доступный «обработчик пересылки», который перенаправляет все на обычный объект (или, конечно, экземпляр), так что прокси ведет себя как обычный объект. Расширяя обработчик (в данном случае часть get
), вы можете легко проложить маршрут Array.prototype
способами следующим образом.
Таким образом, всякий раз, когда любого свойства (с именем name
) является быть неправдоподобным, путь кода выглядит следующим образом:
- Попробуйте возвращение
inst[name]
.
- В противном случае попробуйте вернуть функцию, которая применяет
Array.prototype[name]
к экземпляру с данными аргументами для этой функции.
- В противном случае просто верните
undefined
.
Если вы хотите играть с прокси, вы можете использовать последнюю версию V8, например, в ночных сборках Chromium (убедитесь, что для работы в качестве chrome --js-flags="--harmony"
). Опять же, прокси не доступны для «нормального» использования, потому что они относительно новы, меняют многие фундаментальные части JavaScript и на самом деле еще официально не указаны (все еще черновики).
Это простая диаграмма того, как это происходит (inst
- это фактически прокси-сервер, к которому был перенесен экземпляр). Обратите внимание, что это только иллюстрирует , получив имущество; все остальные операции просто передаются прокси-сервером из-за немодифицированного обработчика пересылки.
код прокси может быть следующим:
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"
Обработчик экспедиторская доступна на 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); }
};
это звучит, как вы думаете о JavaScript [ "pollyfills" или "проставки"] (http://remysharp.com/2010/10/08/what-is-a-polyfill/) –
кстати обычно более элегантно и надежно ловить исключения с помощью блока try-catch. –