为了解决我们的可变性问题,Immutable.js必须提供两个核心可变类型(Object和Array)的不可变版本。
Immutable.Map
Map
是JavaScript对象结构的不可变版本。 由于JavaScript对象具有简洁的对象字面量语法,它通常用作键值存储,key
为string
类型。 此模式紧跟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.delete
和Map.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
来向列表中添加项目,就像我们在数组上调用它一样。 但是因为我们创建了一个新的副本,我们必须重新绑定变量。 当我们想更新特定索引中的项目时,我们有相同的 set
和 update
调用。 我们还可以访问数组函数,如map
,reduce
还支持像我们这里使用的额外方法zipWith
。