Подтвердить что ты не робот

Создайте класс, простирающийся от ES6 Map

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

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

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

Метод Map.prototype.forEach вызывает несовместимый приемник

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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.

См. также Как продлить обещание.

Ответ 3

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

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

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

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

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){
	var $fnMapGet = function(key){
		console.log('get 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('set 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('*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')
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)
}

Ответ 4

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

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

Однако выполнение следующего с Babel бесполезно:

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

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

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