我如何使用facebook/immutable-js自定义不可变类型

How do I make a custom immutable type using facebook/immutable-js?

本文关键字:自定义 不可变 类型 immutable-js 何使用 facebook      更新时间:2023-09-26

我试图使一个简单的不可变的间隔类型。它有起始和结束属性。有没有一种直接的方法来利用immutable-js库来创建自己的自定义不可变类型?

我当前尝试使用包含不可变的模式。映射和不可变。列表不太好用。例如,difference应该返回一个不可变的Interval列表,但是因为它创建了Interval的新实例,所以它返回的list不会传递相等值。

感觉我采取了错误的方法,但我不确定是怎么回事。我可以很容易地用Immutable.Map表示数据,但是我想把difference包含在它的公共接口中,而不包括一般的Map方法,如get和set。也就是说,我希望它是不可变的,但它是自己的类型,有自己的接口。

var Immutable = require('immutable')
function Interval(start, end) {
  this.value = Immutable.Map({
    start: start,
    end: end
  })
}
Interval.prototype.start = function () {
  return this.value.get('start')
}
Interval.prototype.end = function () {
  return this.value.get('end')
}
Interval.prototype.equals = function(interval) {
  return this.value.equals(interval.value)
}
Interval.prototype.difference = function (subtrahend) {
  var nonoverlapping = subtrahend.start() > this.end() || subtrahend.end() < this.start()
  return nonoverlapping ? Immutable.List.of(this) :
    Immutable.List.of(
      new Interval(this.start(), subtrahend.start()),
      new Interval(subtrahend.end(), this.end())
    )
    .filter(function(interval) {
      return interval.end() > interval.start()
    })
}
一些测试:

it('should return an empty array for an exact difference', function() {
  var interval = new Interval(80, 90)
  interval.difference(interval)
    .equals(Immutable.List())
    .should.be.true
})
it('should return an array with the same Interval for a missed difference', function() {
  var interval = new Interval(80, 90)
  console.log(interval.difference(new Interval(50, 60)).toJS());
  interval.difference(new Interval(50, 60))
    .equals(Immutable.List.of([
      { value: Immutable.Map({ start: 80, end: 90 }) }
    ]))
    .should.be.true
})

为什么不使用Record type:

var Interval = Immutable.Record({start: 0, end: 1})

为您创建记录类型,分别用默认值0和1定义startend属性。

现在要创建Interval的实例,只需执行:

var newInterval = new Interval() //will have default values above
var newInterval = new Interval({start: 10, end: 30)

注意Record类型的实例只是值类型,所以它们应该只包含数据。当然,您可以定义对该数据值进行操作的函数。

var difference = function (interval1, interval2) {
  var nonoverlapping = interval2.start > interval1.end || interval2.end < interval1.start
  return nonoverlapping ? Immutable.List.of(interval1) :
    Immutable.List([
      new Interval({start: interval1.start, end: interval2.start}),
      new Interval({start: interval2.end, end: interval1.end})
    ])
    .filter(function(interval) {
      return interval.end > interval.start
    });
}
使用Immutable.is(interval1, interval2) ,您可以免费获得相等性

有关记录的更多信息,请键入:http://facebook.github.io/immutable-js/docs/#/Record

你可以使用" extenable -immutable"库

那就简单了:

const {Map} = require('extendable-immutable');
class Interval extends Map {
    constructor(start, end) {
        super({
            value: new Map({start: start, end: end})
        });
    }
    start() {
        return this.get('value').get('start');
    }
    end() {
        return this.get('value').get('end');
    }
    //...
}

如果您像我一样选择方法(如start, end)而不是字段,我不建议您记录。如果您使用Record实现它,您将拥有自动公开的字段。

您可能想要使用_value字段创建您的Record自定义类,而不是暴露value字段,然而这并不总是可能的。看到:

https://github.com/facebook/immutable-js/issues/970

实现valueOf方法

例如:

Interval.prototype.valueOf = function() {
  return Map({ class: Interval, value: this.value });
}

和测试:

Immutable.is(new Interval(80, 90), new Interval(80, 90)); // true

另一个选择是通过实现方法equalshashCode将类转换为ValueObject(使用函数Immutable.isImmutable.hash来实现)。