【问题标题】:How do I make a custom immutable type using facebook/immutable-js?如何使用 facebook/immutable-js 制作自定义的不可变类型?
【发布时间】:2015-08-04 17:55:51
【问题描述】:

我正在尝试制作一个简单的不可变 Interval 类型。它有一个 start 和一个 end 属性。有没有一种直接的方法可以利用 immutable-js 库来创建您自己的自定义不可变类型?

我目前使用 Immutable.Map 和 Immutable.List 的包含模式的尝试效果不佳。例如,difference 应该返回一个不可变的 Interval 列表,但是因为它创建了新的 Interval 实例,所以它返回的 List 不会通过相等性。

感觉好像我采取了错误的方法,但我不确定如何。我可以很容易地用Immutable.Map 表示数据,但我想在它的公共接口中包含difference,而不是包含像get 和set 这样的通用Map 方法。即我希望它是不可变的,但它是具有自己接口的自己的类型。

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
})

【问题讨论】:

    标签: javascript immutability immutable.js


    【解决方案1】:

    为什么不用Record类型:

    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

    【讨论】:

      【解决方案2】:

      您可以使用"extendable-immutable" library

      那么就很简单了:

      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');
          }
      
          //...
      }
      

      如果你在像我这样的字段上选择方法(例如开始、结束),我不推荐记录。如果您使用 Record 实现它,您将拥有自动公开的字段。

      您可能希望使用 _value 字段创建 Record 自定义类,而不是公开 value 字段,但这并不总是可行的。见:

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

      【讨论】:

        【解决方案3】:

        实现方法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 来实现)。

        【讨论】:

          猜你喜欢
          • 2019-03-19
          • 1970-01-01
          • 2020-07-18
          • 2016-03-31
          • 1970-01-01
          • 2015-06-27
          • 2021-01-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多