创建一个 class 扩展自 ES6 Map
Create a class extending from ES6 Map
尝试在 ES6 地图上使用自定义 get/set 功能。目前正在使用 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);
}
}
不确定我是语法错误还是缺少某种实现。但是我收到以下错误:
Method Map.prototype.forEach called on incompatible reciever
Babel 明确表示他们不支持扩展内置 类。参见 http://babeljs.io/docs/usage/caveats/#classes。然而,原因并不像 "limitations in ES5" 那么简单,因为 Map
不是 ES5 的特性。似乎 Map 的实现不支持基本模式,例如
Map.prototype.set.call(mymap, 'key', 1);
这基本上就是 Babel 在这种情况下生成的内容。问题在于包括 V8 在内的 Map 实现过于严格,并检查 Map.set.call
调用中的 this
是否恰好是一个 Map,而不是在其原型链中包含 Map。
同样适用于 Promise。
是的,在 Proxies 全面生效之前,实现您想要做的事情的唯一方法是自己隐藏 built-in 方法对 Map/Set 等。
例如,如果您有这样的地图:
var myMap = new Map([ ['key1', 'value1'], ['key2', 'value2']])
您必须有一些包装器才能将其传递给添加 built-in 方法,例如 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
这将增加在地图上执行您自己的自定义 set/get 的能力,虽然不如子类化 Map 好,也不像 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)
}
你应该使用老办法:
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
绑定。
很遗憾,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');
但是,运行使用 Babel 进行以下操作是没有用的:
class Store extends Map {
constructor(...args) {
super(...args);
return this;
}
}
您将在尝试调用 (new Store(['a','1'])).get('a')
时抛出错误。这让我感到恐惧,像 Map
这样重要的东西会被 Babel 的人们完全忽视。
这是我的推荐。 几年来我一直在做的是 创建一个 JavaScript Class,你可以随身携带它去参加任何演出或项目。将其命名为“Dictionary
”,如果您的环境支持 Map
并且您需要一个地图,只需将 Map
包裹起来——为了性能起见。如果需要继承自Map
,则继承自Dictionary
。实际上,我有自己的私人存储库,其中包含各种算法和数据结构,我走到哪里都会随身携带,但您也可以找到 public 存储库来完成同样的事情。有点痛苦,但这样你就不会 100% 依赖于每个框架和环境的相同事物。
或者,您可以在 MapWrapper 对象中组合 Map() class 并公开您自己的 API。对象组合的概念在 5 年前并没有被广泛使用,因此,问题和答案与继承相关联并纠缠在一起。
尝试在 ES6 地图上使用自定义 get/set 功能。目前正在使用 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);
}
}
不确定我是语法错误还是缺少某种实现。但是我收到以下错误:
Method Map.prototype.forEach called on incompatible reciever
Babel 明确表示他们不支持扩展内置 类。参见 http://babeljs.io/docs/usage/caveats/#classes。然而,原因并不像 "limitations in ES5" 那么简单,因为 Map
不是 ES5 的特性。似乎 Map 的实现不支持基本模式,例如
Map.prototype.set.call(mymap, 'key', 1);
这基本上就是 Babel 在这种情况下生成的内容。问题在于包括 V8 在内的 Map 实现过于严格,并检查 Map.set.call
调用中的 this
是否恰好是一个 Map,而不是在其原型链中包含 Map。
同样适用于 Promise。
是的,在 Proxies 全面生效之前,实现您想要做的事情的唯一方法是自己隐藏 built-in 方法对 Map/Set 等。
例如,如果您有这样的地图:
var myMap = new Map([ ['key1', 'value1'], ['key2', 'value2']])
您必须有一些包装器才能将其传递给添加 built-in 方法,例如 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
这将增加在地图上执行您自己的自定义 set/get 的能力,虽然不如子类化 Map 好,也不像 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)
}
你应该使用老办法:
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
绑定。
很遗憾,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');
但是,运行使用 Babel 进行以下操作是没有用的:
class Store extends Map {
constructor(...args) {
super(...args);
return this;
}
}
您将在尝试调用 (new Store(['a','1'])).get('a')
时抛出错误。这让我感到恐惧,像 Map
这样重要的东西会被 Babel 的人们完全忽视。
这是我的推荐。 几年来我一直在做的是 创建一个 JavaScript Class,你可以随身携带它去参加任何演出或项目。将其命名为“Dictionary
”,如果您的环境支持 Map
并且您需要一个地图,只需将 Map
包裹起来——为了性能起见。如果需要继承自Map
,则继承自Dictionary
。实际上,我有自己的私人存储库,其中包含各种算法和数据结构,我走到哪里都会随身携带,但您也可以找到 public 存储库来完成同样的事情。有点痛苦,但这样你就不会 100% 依赖于每个框架和环境的相同事物。
或者,您可以在 MapWrapper 对象中组合 Map() class 并公开您自己的 API。对象组合的概念在 5 年前并没有被广泛使用,因此,问题和答案与继承相关联并纠缠在一起。