2015-04-03 3 views
16

Попытка уйти с пользовательской функцией get/set на ES6 Maps. В настоящее время используется Babel, чтобы перевести мой код на ES5.Создайте класс, простирающийся от ES6. Карта

Chrome Version 41.0.2272.101 m

class MyMap extends Map { 
    get(key) { 
     if (!this.has(key)) { throw new Error(...); } 
     return super.get(key); 
    } 

    set(key) { 
     if (this.has(key)) { throw new Error(...); } 
     return super.set(key); 
    } 
} 

Не уверен, что, если я только что получил синтаксис неправильно, или я пропускаю реализацию какой-то. Но я получаю следующую ошибку:

Method Map.prototype.forEach called on incompatible reciever

+0

Я просто получаю 'Объявления с блочной областью (let, const, function, class) еще не поддерживаются за пределами строгого режима. – thefourtheye

+0

И я получаю' Uncaught SyntaxError: Unexpected reserved word'. Возможно, Chrome не поддерживает расширение встроенных классов. –

+0

Yup, in io.js Я могу скомпилировать его без каких-либо ошибок (хотя я должен был использовать '' строгое "' вверху) – thefourtheye

ответ

9

Вавилон явно заявляет, что не поддерживает расширение встроенных классов. См. http://babeljs.io/docs/usage/caveats/#classes. Причины не так просты, как «ограничения в ES5», однако, поскольку Map не является функцией ES5 для начала. Представляется, что реализация карты не поддерживают основные закономерности, такие как

Map.prototype.set.call(mymap, 'key', 1); 

, который, по сути, то, что Бабель порождает в этом случае. Проблема заключается в том, что реализации Map, включая V8, являются чрезмерно ограничительными и проверяют, что this в вызове Map.set.call представляет собой именно карту, а не карту в своей цепочке прототипов.

То же самое касается обещания.

+3

Итак, вам нужно повторно реализовать каждый метод карты как прокси-сервер для внутренней карты? Это кажется очень застенчивым. Кажется, что бабульцы не должны фактически использовать Карты. ┻━┻ (ლ (⌒-⌒ლ) – Sukima

+0

Вы можете реализовать свои собственные методы, такие как 'guardedSet' и guardedGet' на прототипе' Map'. –

0

Yup, до тех пор, пока Proxies не придет в полную силу, единственный способ добиться того, что вы пытаетесь сделать, - это затенять встроенные методы на карте/множестве и т. Д. Самостоятельно.

Например, если у вас есть карта, как так:

var myMap = new Map([ ['key1', 'value1'], ['key2', 'value2']]) 

Вы должны были бы иметь некоторую обертку, чтобы передать его в добавить встроенные методы, например, для получения/установки:

function proxify(obj){ 
    var $fnMapGet = function(key){ 
     console.log('%cmap get', 'color:limegreen', 'key:', key) 
     if(!Map.prototype.has.call(this, key)){ 
      throw(new Error('No such key: '+ key)) 
     } else { 
      return Map.prototype.get.call(this, key) 
     } 
    } 
    var $fnMapSet = function(key, value){ 
     console.log('%cmap set', 'color:tomato', 'key:', key, 'value:', value) 
     if(Map.prototype.has.call(this, key)){ 
      throw(new Error('key is already defined: ' + key)) 
     } else { 
      if(Map.prototype.get.call(this, key) == value){ 
       console.log('%cmap set', 'color:tomato', '*no change') 
       return this 
      } 
      return Map.prototype.set.call(this, key, value) 
     } 
    } 

    Object.defineProperty(obj, 'get', { 
     get(){ 
      return $fnMapGet 
     } 
    }) 
    Object.defineProperty(obj, 'set', { 
     get(){ 
      return $fnMapSet 
     } 
    }) 

    return obj 
} 

Итак:

proxify(myMap) 

myMap.get('key1') // <= "value1" 
myMap.get('key2') // <= "value2" 
myMap.get('key3') // <= Uncaught Error: No such key: key3 
myMap.set('key3', 'value3') // <= Map {"key1" => "value1", "key2" => "value2", "key3" => "value3"} 
myMap.set('key3', 'another value3') // <= Uncaught Error: key is already defined: key3 

Это было бы добавить возможность сделать свой собственный набор/получить на карте, не почти такая же приятная, как и подклассическая карта, и не так проста, как прокси-серверы es6, но она по крайней мере работает.

Полный фрагмент кода работает ниже:

var myMap = new Map([ ['key1', 'value1'], ['key2', 'value2']]) 
 

 
function proxify(obj){ 
 
\t var $fnMapGet = function(key){ 
 
\t \t console.log('get key:', key) 
 
\t \t if(!Map.prototype.has.call(this, key)){ 
 
\t \t \t throw(new Error('No such key: '+ key)) 
 
\t \t } else { 
 
\t \t \t return Map.prototype.get.call(this, key) 
 
\t \t } 
 
\t } 
 
\t var $fnMapSet = function(key, value){ 
 
\t \t console.log('set key:', key, 'value:', value) 
 
\t \t if(Map.prototype.has.call(this, key)){ 
 
\t \t \t throw(new Error('key is already defined: ' + key)) 
 
\t \t } else { 
 
\t \t \t if(Map.prototype.get.call(this, key) == value){ 
 
\t \t \t \t console.log('*no change') 
 
\t \t \t \t return this 
 
\t \t \t } 
 
\t \t \t return Map.prototype.set.call(this, key, value) 
 
\t \t } 
 
\t } 
 

 
\t Object.defineProperty(obj, 'get', { 
 
\t \t get(){ 
 
\t \t \t return $fnMapGet 
 
\t \t } 
 
\t }) 
 
\t Object.defineProperty(obj, 'set', { 
 
\t \t get(){ 
 
\t \t \t return $fnMapSet 
 
\t \t } 
 
\t }) 
 

 
\t return obj 
 
} 
 

 
proxify(myMap) 
 
myMap.get('key1') 
 
myMap.get('key2') 
 
try { 
 
    myMap.get('key3') 
 
} catch(ex){ 
 
    console.warn('error:', ex.message) 
 
} 
 
myMap.set('key3', 'value3') 
 
try { 
 
    myMap.set('key3', 'another value3') 
 
} catch(ex){ 
 
    console.warn('error:', ex.message) 
 
}

1

Вы должны использовать старый добрый путь:

function ExtendedMap(iterable = []) { 
    if (!(this instanceof ExtendedMap)) { 
    throw new TypeError("Constructor ExtendedMap requires 'new'"); 
    } 

    const self = (Object.getPrototypeOf(this) === Map.prototype) 
    ? this 
    : new Map(iterable); 
    Object.setPrototypeOf(self, ExtendedMap.prototype); 

    // Do your magic with `self`... 

    return self; 
} 

util.inherits(ExtendedMap, Map); 
Object.setPrototypeOf(ExtendedMap, Map); 

ExtendedMap.prototype.foo = function foo() { 
    return this.get('foo'); 
} 

Затем использовать new как обычно:

const exMap = new ExtendedMap([['foo', 'bar']]); 
exMap instanceof ExtendedMap; // true 
exMap.foo(); // "bar" 

Обратите внимание, что конструктор ExtendedMap игнорирует привязку this, которая не является Map.

См. Также How to extend a Promise.

-1

К сожалению, Babel не поддерживает его.Странная вещь, вы можете выполнить следующую команду в консоли:

clear(); 

var Store = function Store(data) { 
    // var _map = new Map(data); 
    this.get = function get(key) { 
     console.log('#get', key); 
     return S.prototype.get.call(S.prototype, key); // or return _map.get(key); 
    }; 
    this.set = function set(key, value) { 
     S.prototype.set.call(S.prototype, key, value); // or _map.set(key, value); 
     return this; 
    }; 
}; 
Store.prototype = new Map(); // we could just wrap new Map() in our constructor instead 

var s = new Store(); 

s.set('a', 1); 
s.get('a'); 

Однако, выполнив следующую команду с Вавилонской бесполезно:

class Store extends Map { 
    constructor(...args) { 
     super(...args); 
     return this; 
    } 
} 

Вы сгенерирует ошибку, пытаясь вызвать (new Store(['a','1'])).get('a'). Это ужаснет меня, что что-то столь же важное, как Map, было бы совершенно уволено людьми в Вавилоне.

Вот что я рекомендую. То, что я делал в течение нескольких лет, - этосоздать класс JavaScript, который вы можете объединить с собой на любой концерт или проект. Назовите его «Dictionary», и если ваша среда поддерживает Map, и вам нужна карта, просто оберните Map - ради выполнения. Если вам необходимо наследовать от Map, наследуйте от Dictionary. У меня на самом деле есть собственное частное репо с различными алгоритмами & данных, которые я привожу везде, куда бы я ни пошел, но вы также можете найти публичные репозитории, которые выполняют одно и то же. Вид боли, но таким образом вы не полагаетесь на 100% на одно и то же для каждой среды &.

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