简体   繁体   English

为什么在chrome / node中没有Map子类?

[英]Why isn't Map subclassable in chrome/node?

So ES 6 is bringing us Maps (and none too soon). 所以ES 6给我们带来了地图 (并且没有太快)。 Wishing to subclass Map for my own nefarious purposes, I tried the following (abbreviated for clarity): 为了我自己的邪恶目的,我希望将Map子类化,我尝试了以下(为了清晰起见):

function Foo() {
    return Map.apply(this, [].slice.call(arguments));
}

var bar = new Foo();

In V8 environments this throws an Error "Map constructor not called with 'new'". 在V8环境中,这会抛出错误“未使用'new'调用Map构造函数”。 Why? 为什么? SpiderMonkey gets this 'right': SpiderMonkey得到了这个'正确':

Map.call({}, [['foo', 'bar']]).get('foo');

will, as expected, yield 'bar'. 将如预期的那样产生'bar'。 In both SpiderMonkey and V8 attempting something like 在SpiderMonkey和V8中尝试类似的东西

function Foo() {};
Foo.prototype = new Map();
var bar = new Foo();
bar.set('foo', 'bar');

will fail: 'method set called on incompatible object'. 将失败:'方法集调用不兼容的对象'。 So there goes that inheritance pattern. 那就是继承模式。 As far as I can tell from the spec (I don't have much spec-foo), actual Map objects have internal properties that are not accessible that are required for them to work properly. 据我所知,从规范 (我没有太多spec-foo),实际的Map对象具有无法访问的内部属性,这些属性是他们正常工作所必需的。 But why does V8 throw an error on the first pattern? 但是为什么V8会在第一个模式上抛出错误? This seems like an odd decision, especially when it works as expected in FF. 这似乎是一个奇怪的决定,特别是当它在FF中按预期工作时。

UPDATE: I noticed that FF and chrome both have Object.setPrototypeOf implemented. 更新:我注意到FF和chrome都实现了Object.setPrototypeOf Just in case anyone stumbles across this and thinks of that, I can tell you it fails. 万一有人偶然发现并想到这一点,我可以告诉你它失败了。 Both of the following fail for different reasons: 以下两种因以下原因而失败:

//this totally fails, even for custom constructors/objects
var Foo = function(){};
Object.setPrototypeOf(Foo, Map.prototype);
var bar = new Foo(); //bar doesn't have any of the Map properties/methods

//this one has the methods but still throws the 'incompatible object'
//err. Also fails with new Map() instead of Map.prototype
var bar = Object.setPrototypeOf({}, Map.prototype); 

TL;DR TL; DR

There are basically four ways to extend map (some from answers/comments below): 基本上有四种扩展地图的方法(一些来自下面的答案/评论):

  1. Add methods to Map.prototype (shame on you). 向Map.prototype添加方法(羞辱你)。
  2. Factory/constructor makes objects with an internal Map instance that you delegate to Factory / constructor使用您委派的内部Map实例创建对象
  3. Mixin fn that copies properties onto Maps: Mixin fn将属性复制到地图上:
function Foo(){this.bar = 'boo!'}
var baz = new Map();
Foo.call(baz);
baz.bar; //yields 'boo!'
baz.set('5', 5).get('5'); //yields 5

Or just wait for ES 6 classes to hit the platform(s) you care about 或者只是等待ES 6课程到达你关心的平台

In V8 environments this throws an Error "Map constructor not called with 'new'". 在V8环境中,这会抛出错误“未使用'new'调用Map构造函数”。 Why? 为什么?

Because new ES6 classes (including builtin ones) are supposed to be only constructable with new . 因为新的ES6类(包括内置类)应该只能用new构造。

SpiderMonkey gets this 'right' SpiderMonkey得到这个'正确'

Not exactly. 不完全是。 The spec explicitly says 该规范明确指出

Map is not intended to be called as a function and will throw an exception when called in that manner. Map不应该作为函数调用,并且在以这种方式调用时会抛出异常。

Wishing to subclass Map 希望子类化Map

Yes, that's the appropriate thing: 是的,这是合适的:

The Map constructor is designed to be subclassable. Map构造函数设计为可子类化。 It may be used as the value in an extends clause of a class definition. 它可以用作class定义的extends子句中的值。 Subclass constructors that intend to inherit the specified Map behaviour must include a super call to the Map constructor to create and initialize the subclass instance with the internal state necessary to support the Map.prototype built-in methods. 打算继承指定Map行为的子类构造函数必须包含对Map构造函数的super调用,以使用支持Map.prototype内置方法所需的内部状态创建和初始化子类实例。

So you'll want to use 所以你会想要使用

class Foo extends Map {
    // default constructor
}
var bar = new Foo();
bar.set('foo', 'bar');

Actually, it doesn't really 'work' in FF, since FF also allows to create maps simply by calling Map() . 实际上,它在FF中并没有真正起作用,因为FF也允许通过调用Map()来创建地图。

However, according to http://kangax.github.io/compat-table/es6/ we do not have compatibility for subclassing in modern browser (fascinating enough, IE has some support here). 但是,根据http://kangax.github.io/compat-table/es6/,我们在现代浏览器中没有子类化的兼容性(非常有趣,IE在这里有一些支持)。

tl;dr V8/SpiderMonkey are not fully ES6 compatible yet. tl; dr V8 / SpiderMonkey尚未完全兼容ES6。

You should be able to do something like this: 你应该可以做这样的事情:

function Foo() {
    return new (Map.bind(this, [].slice.call(arguments)));
}

var bar = new Foo();

For anyone running into this in 2018: 对于2018年遇到这种情况的人:

import ES6Map from 'es6-map/polyfill'

class MyMap extends ES6Map {
  constructor () {
    super()
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM