2015-04-03 85 views
16

试图脱离ES6地图上的自定义获取/设置功能。目前使用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,函数,类)不支持严格模式外' – thefourtheye 2015-04-03 14:53:12

+0

而且我得到'未捕获的SyntaxError:意外的保留字'。 Chrome可能不支持扩展内置类。 – 2015-04-03 14:57:45

+0

是的,在io.js中,我能够无误地编译它(尽管我必须在顶部使用strict)' – thefourtheye 2015-04-03 14:58:50

回答

9

Babel明确声明它们不支持扩展内置类。见http://babeljs.io/docs/usage/caveats/#classes。原因并不像“ES5中的限制”那么简单,但是,因为Map不是ES5的特性。看起来Map的实现不支持基本模式,如

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

这实质上是巴贝尔在这种情况下产生的。问题是包括V8在内的Map的实现过于严格,并且检查Map.set.call调用中的this恰恰是一个Map,而不是在其原型链中包含Map。

同样适用于承诺。

+3

所以你必须重新实现Map的每个方法作为内部Map的代理?这似乎很棘手。似乎巴贝尔人不能真正使用地图。 ┻━┻(ლ(⌒-⌒ლ) – Sukima 2016-02-16 22:15:08

+0

您可以在'Map'原型上实现自己的方法,例如'guardedSet'和guardedGet'。 – 2016-02-17 04:44:29

0

是的,直到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 

这将添加到做自己的自定义设置的能力/获取地图上,不几乎和子类Map一样好,也不像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构造函数忽略不是Map的任何this绑定。请参阅How to extend a Promise

-1

不幸的是,巴贝尔不支持它。奇怪的是,你可以运行在您的控制台以下:

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%地依靠同样的东西。