2015-04-03 80 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%地依靠同樣的東西。