Immutable.js基础

为了解决我们的可变性问题,Immutable.js必须提供两个核心可变类型(Object和Array)的不可变版本。

Immutable.Map

Map是JavaScript对象结构的不可变版本。 由于JavaScript对象具有简洁的对象字面量语法,它通常用作键值存储,keystring类型。 此模式紧跟map数据结构。 让我们重温前面的例子,但改用Immutable.Map

import * as Immutable from 'immutable';
let movie1 = Immutable.Map<string, any>({
  name: 'Star Wars',
  episode: 7
});
let movie2 = movie1;
movie2 = movie2.set('episode', 8);
console.log(movie1.get('episode')); // writes 7
console.log(movie2.get('episode')); // writes 8

不是直接将对象字面量绑定到movie1,而是将其作为参数传递给Immutable.Map。 这改变了我们如何与movie1的属性交互。

要获取属性的值,我们调用get方法,传递我们想要的属性名称,就像我们如何使用对象的字符串索引器。

要设置属性的值,我们调用set方法,传递属性名和新值。 注意,它不会改变现有的Map对象 - 它返回一个带有更新属性的新对象,因此我们必须将movie2变量重新绑定到新对象。

Map.merge

有时我们想更新多个属性。 我们可以使用merge方法做到这一点。

let baseButton = Immutable.Map<string, any>({
  text: 'Click me!',
  state: 'inactive',
  width: 200,
  height: 30
});
let submitButton = baseButton.merge({
  text: 'Submit',
  state: 'active'
});
console.log(submitButton);
// writes { text: 'Submit', state: 'active', width: 200, height: 30 }

嵌套对象

Immutable.Map以浅的方式包装对象,这意味着如果你有一个对象的属性绑定到可变类型,那么这些属性可以被改变。

let movie = Immutable.Map({
    name: 'Star Wars',
    episode: 7,
    actors: [
        { name: 'Daisy Ridley', character: 'Rey'},
        { name: 'Harrison Ford', character: 'Han Solo' }
    ],
    mpaa: {
        rating: 'PG-13',
        reason: 'sci-fi action violence'
    }
});
movie.get('actors').pop();
movie.get('mpaa').rating = 'PG';
console.log(movie.toObject());
/* writes
{ name: 'Star Wars',
  episode: 7,
  actors: [ { name: 'Daisy Ridley', character: 'Rey' } ],
  mpaa: { rating: 'PG', reason: 'sci-fi action violence' } }
  */

要避免此问题,请改用Immutable.fromJS

let movie = Immutable.fromJS({
    name: 'Star Wars',
    episode: 7,
    actors: [
        { name: 'Daisy Ridley', character: 'Rey'},
        { name: 'Harrison Ford', character: 'Han Solo' }
    ],
    mpaa: {
        rating: 'PG-13',
        reason: 'sci-fi action violence'
    }
});
movie.get('actors').pop();
movie.get('mpaa').rating = 'PG';
console.log(movie.toObject());
/* writes
{ name: 'Star Wars',
  episode: 7,
  actors: List [ Map { "name": "Daisy Ridley", "character": "Rey" }, Map { "name": "Harrison Ford", "character": "Han Solo" } ],
  mpaa: Map { "rating": "PG-13", "reason": "sci-fi action violence" } }
*/

所以,当你想修改movie.mpaa.rating。 你可能会想这样做:movie = movie.get('mpaa').set('rating','PG')。 然而,set将总是返回调用Map实例,在这种情况下返回映射绑定到mpaa键,而不是你想要的电影。 我们必须使用setIn方法来更新嵌套属性。

let movie = Immutable.fromJS({
    name: 'Star Wars',
    episode: 7,
    actors: [
        { name: 'Daisy Ridley', character: 'Rey'},
        { name: 'Harrison Ford', character: 'Han Solo' }
    ],
    mpaa: {
        rating: 'PG-13',
        reason: 'sci-fi action violence'
    }
});
movie = movie
  .update('actors', actors => actors.pop())
  .setIn(['mpaa', 'rating'], 'PG');
console.log(movie.toObject());
/* writes
{ name: 'Star Wars',
  episode: 7,
  actors: List [ Map { "name": "Daisy Ridley", "character": "Rey" } ],
  mpaa: Map { "rating": "PG", "reason": "sci-fi action violence" } }
*/

我们还添加了一个对Map.update的调用,与set不同,它接受一个函数作为第二个参数,而不是一个值。 此函数接受该键处的现有值,并且必须返回该键的新值。

删除键

可以使用Map.deleteMap.deleteIn方法从Maps中删除键。

let movie = Immutable.fromJS({
    name: 'Star Wars',
    episode: 7,
    actors: [
        { name: 'Daisy Ridley', character: 'Rey'},
        { name: 'Harrison Ford', character: 'Han Solo' }
    ],
    mpaa: {
        rating: 'PG-13',
        reason: 'sci-fi action violence'
    }
});
movie = movie.delete('mpaa');
console.log(movie.toObject());
/* writes
{ name: 'Star Wars',
  episode: 7,
  actors: List [ Map { "name": "Daisy Ridley", "character": "Rey" }, Map { "name": "Harrison Ford", "character": "Han Solo" } ] }
*/

Map是可迭代的

Immutable.js中的Maps是可迭代的,这意味着你可以在Maps中使用map, filter, reduce等遍历每个键值对。

let features = Immutable.Map<string, boolean>({
    'send-links': true,
    'send-files': true,
    'local-storage': true,
    'mirror-notifications': false,
    'api-access': false
});
let myFeatures = features.reduce((providedFeatures, provided, feature) => {
    if(provided)
        providedFeatures.push(feature);
  return providedFeatures;
}, []);
console.log(myFeatures); // [ 'send-links', 'send-files', 'local-storage' ]
const mapMap = Immutable.Map({ a: 0, b: 1, c: 2 });
mapMap.map(i => i * 30);
const mapFilter = Immutable.Map({ a: 0, b: 1, c: 2 });
mapFilter.filter(i => i % 2);
const mapReduce = Immutable.Map({ a: 10, b: 20, c: 30 });
mapReduce.reduce((acc, i) => acc + i, 0);

Immutable.List

List是JavaScript数组结构的不可变版本。

let movies = Immutable.fromJS([ // again use fromJS for deep immutability
  {
    name: 'The Fellowship of the Ring',
    released: 2001,
    rating: 8.8
  },
  {
    name: 'The Two Towers',
    released: 2002,
    rating: 8.7
  }
]);
movies = movies.push(Immutable.Map({
    name: 'The Return of the King',
    released: 2003
}));
movies = movies.update(2, movie => movie.set('rating', 8.9)); // 0 based
movies = movies.zipWith(
  (movie, seriesNumber) => movie.set('episode', seriesNumber),
  Immutable.Range(1, movies.size + 1) // size property instead of length
);
console.log(movies);
/* writes
List [
  Map { "name": "The Fellowship of the Ring", "released": 2001, "rating": 8.8, "episode": 1 },
  Map { "name": "The Two Towers", "released": 2002, "rating": 8.7, "episode": 2 },
  Map { "name": "The Return of the King", "released": 2003, "rating": 8.9, "episode": 3 } ]
  */

这里我们再次使用Immutable.fromJS调用,因为我们有对象存储在数组中。 我们调用push来向列表中添加项目,就像我们在数组上调用它一样。 但是因为我们创建了一个新的副本,我们必须重新绑定变量。 当我们想更新特定索引中的项目时,我们有相同的 setupdate调用。 我们还可以访问数组函数,如mapreduce还支持像我们这里使用的额外方法zipWith