2012-02-01 2 views
6

Прежде чем я закричаю за попытку чего-то столь безрассудного, позвольте мне сказать вам, что я не буду делать этого в реальной жизни, и это академический вопрос.Резюме с ошибкой

Предположим, я пишу библиотеку, и я хочу, чтобы мой объект мог составлять методы по мере необходимости.

Например, если вы хотите, чтобы вызвать метод .slice(), и у меня не было, то один window.onerror обработчик будет стрелять для меня

Во всяком случае, я играл с этим here

window.onerror = function(e) { 
    var method = /'(.*)'$/.exec(e)[1]; 
    console.log(method); // slice 
    return Array.prototype[method].call(this, arguments); // not even almost gonna work 
}; 

var myLib = function(a, b, c) { 
    if (this == window) return new myLib(a, b, c); 
    this[1] = a; this[2] = b; this[3] = c; 
    return this; 
}; 

var obj = myLib(1,2,3); 

console.log(obj.slice(1)); 

Также (может быть, я должен начать новый вопрос) могу ли я изменить свой конструктор, чтобы получить неопределенное количество аргументов?

var myLib = function(a, b, c) { 
    if (this == window) return new myLib.apply(/* what goes here? */, arguments); 
    this[1] = a; this[2] = b; this[3] = c; 
    return this; 
}; 

КСТАТИ Я знаю, что я могу загрузить свои объекты с

['slice', 'push', '...'].forEach(function() { myLib.prototype[this] = [][this]; }); 

Это не то, что я ищу

+0

это звучит, как вы думаете о JavaScript [ "pollyfills" или "проставки"] (http://remysharp.com/2010/10/08/what-is-a-polyfill/) –

+1

кстати обычно более элегантно и надежно ловить исключения с помощью блока try-catch. –

ответ

4

Как вы спрашивали академический вопрос, я полагаю, совместимость браузера не является вопрос. Если это действительно так, я бы хотел представить для этого прокси-серверы гармонии. onerror не очень хорошая практика, поскольку это только событие, поднятое, если где-то произошла ошибка. Он должен, если вообще когда-либо, использоваться только в качестве крайней меры. (Я знаю, что вы сказали, что не используете его в любом случае, но onerror просто не очень подходит для разработчиков.)

В основном, прокси-серверы позволяют перехватить большинство основных операций в JavaScript - прежде всего, получить какое-либо свойство, которое полезно здесь. В этом случае вы можете перехватить процесс получения .slice.

Обратите внимание, что прокси-серверы являются «черными дырами» по умолчанию. Они не соответствуют ни одному объекту (например, установка свойства в прокси-сервере просто вызывает ловушку (перехватчик) set, фактическое сохранение, которое вы должны сделать сами). Но есть доступный «обработчик пересылки», который перенаправляет все на обычный объект (или, конечно, экземпляр), так что прокси ведет себя как обычный объект. Расширяя обработчик (в данном случае часть get), вы можете легко проложить маршрут Array.prototype способами следующим образом.

Таким образом, всякий раз, когда любого свойства (с именем name) является быть неправдоподобным, путь кода выглядит следующим образом:

  1. Попробуйте возвращение inst[name].
  2. В противном случае попробуйте вернуть функцию, которая применяет Array.prototype[name] к экземпляру с данными аргументами для этой функции.
  3. В противном случае просто верните undefined.

Если вы хотите играть с прокси, вы можете использовать последнюю версию V8, например, в ночных сборках Chromium (убедитесь, что для работы в качестве chrome --js-flags="--harmony"). Опять же, прокси не доступны для «нормального» использования, потому что они относительно новы, меняют многие фундаментальные части JavaScript и на самом деле еще официально не указаны (все еще черновики).

Это простая диаграмма того, как это происходит (inst - это фактически прокси-сервер, к которому был перенесен экземпляр). Обратите внимание, что это только иллюстрирует , получив имущество; все остальные операции просто передаются прокси-сервером из-за немодифицированного обработчика пересылки.

proxy diagram

код прокси может быть следующим:

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); } 
}; 

 Смежные вопросы

  • Нет связанных вопросов^_^