一段时间的响应式编程的研究,对Rxjs有了一些熟悉。虽然工作中绝大部分时候很少会有复杂的需求用到Rxjs,但是对于Rxjs的思想和响应式编程、观察者模式等相关知识是值得学习的!
而且其中的观察者模式Observable已经列入了JavaScript规范中。Observable 目前处于 stage 1,但它已被 TC39 委员会标记为 “ready to advance” 并获得了浏览器厂商的大力支持,因此有望很快推进到下一阶段。
从Rxjs的operators移植一些到日常开发中的可复现场景。
1.合并数组concatAll
Array.prototype.concatAll = function () {
let results = [];
this.forEach((subArray) => {
results.push.apply(results, subArray);
});
return results;
};
console.log(JSON.stringify([[1, 2, 3], [4, 5, 6], [7, 8, 9]].concatAll()))
//[1,2,3,4,5,6,7,8,9]
当然,js数组的concat方法也可实现,eg
console.log(JSON.stringify([1, 2, 3].concat([4, 5, 6], [7, 8, 9])))
//[1,2,3,4,5,6,7,8,9]
但concatAll是为扩展后文的几个operator而重新定义的合并数组的方法,看后文
2.concatMap
concatMap:map()+concatAll()
Array.prototype.concatMap = function (projectionFunctionThatReturnsArray) {
return this.map(function (item) {
return projectionFunctionThatReturnsArray(item);
}).
// 使用concatAll方法来打平数组
concatAll();
};
let spanishFrenchEnglishWords = [["cero", "rien", "zero"], ["uno", "un", "one"], ["dos", "deux", "two"]];
// map返回三个数组,concatAll返回打平后的一个数组
let allWords = [0, 1, 2].
concatMap(index => spanishFrenchEnglishWords[index]);
console.log(JSON.stringify(allWords))
// ["cero","rien","zero","uno","un","one","dos","deux","two"]
常用场景:打平数组,获取数组中的深层数据,eg:
let movieLists = [
{
name: "Instant Queue",
videos: [
{
"id": 70111470,
"title": "Die Hard",
"boxarts": [
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"bookmark": []
},
{
"id": 654356453,
"title": "Bad Boys",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" },
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"bookmark": [{ id: 432534, time: 65876586 }]
}
]
},
{
name: "New Releases",
videos: [
{
"id": 65432445,
"title": "The Chamber",
"boxarts": [
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"bookmark": []
},
{
"id": 675465,
"title": "Fracture",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture150.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"bookmark": [{ id: 432534, time: 65876586 }]
}
]
}
];
const data = movieLists.concatMap((movieList) => {
return movieList.videos.concatMap((video) => {
return video.boxarts.
filter((boxart) => boxart.width === 150 && boxart.height === 200).
map((boxart) => {
return { id: video.id, title: video.title, boxart: boxart.url };
});
});
});
console.log(JSON.stringify(data))
/* [
{"id":70111470,"title":"Die Hard","boxart":"http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"},
{"id":654356453,"title":"Bad Boys","boxart":"http://cdn-0.nflximg.com/images/2891/BadBoys150.jpg"},
{"id":65432445,"title":"The Chamber","boxart":"http://cdn-0.nflximg.com/images/2891/TheChamber150.jpg"},
{"id":675465,"title":"Fracture","boxart":"http://cdn-0.nflximg.com/images/2891/Fracture150.jpg"}
]
*/
3.reduceArray
即reduce并返回数组,为的是能链式调用,以结合map等方法
// [1,2,3].reduceArray(function(accumulatedValue, currentValue) { return accumulatedValue + currentValue; }); === [6];
// [1,2,3].reduceArray(function(accumulatedValue, currentValue) { return accumulatedValue + currentValue; }, 10); === [16];
Array.prototype.reduceArray = function (combiner, initialValue) {
let counter,
accumulatedValue;
if (this.length === 0) {
return this;
}
else {
if (arguments.length === 1) {
counter = 1;
accumulatedValue = this[0];
}
else if (arguments.length >= 2) {
counter = 0;
accumulatedValue = initialValue;
}
else {
throw "Invalid arguments.";
}
while (counter < this.length) {
accumulatedValue = combiner(accumulatedValue, this[counter])
counter++;
}
return [accumulatedValue];
}
};
let movieLists = [
{
name: "New Releases",
videos: [
{
"id": 70111470,
"title": "Die Hard",
"boxarts": [
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"bookmark": []
},
{
"id": 654356453,
"title": "Bad Boys",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" },
{ width: 140, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"bookmark": [{ id: 432534, time: 65876586 }]
}
]
},
{
name: "Thrillers",
videos: [
{
"id": 65432445,
"title": "The Chamber",
"boxarts": [
{ width: 130, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"bookmark": []
},
{
"id": 675465,
"title": "Fracture",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 120, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture120.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"bookmark": [{ id: 432534, time: 65876586 }]
}
]
}
];
/* 使用concatMap,map,reduceArray来生成以下数组,注:返回的是width*height最小的
[
{"id": 675465,"title": "Fracture","boxart":"http://cdn-0.nflximg.com/images/2891/Fracture120.jpg" },
{"id": 65432445,"title": "The Chamber","boxart":"http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg" },
{"id": 654356453,"title": "Bad Boys","boxart":"http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg" },
{"id": 70111470,"title": "Die Hard","boxart":"http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" }
]; */
const data = movieLists.concatMap(type => {
return type.videos.concatMap(video => {
return video.boxarts.reduceArray((prev, next) => {
return (prev.width * prev.height < next.width * next.height) ? prev : next;
}).map(boxart => {
return { id: video.id, title: video.title, boxart: boxart.url }
})
})
})
console.log(data)
4.zip
给数组添加一个静态方法zip(),zip接收三个参数(第一个数组的元素,第二个数组中与第一个数组index相同的元素,对此两个元素进行的操作).由于zip方法需要2个数组中各自的一个元素,所以zip方法返回的元素个数与输入的两个数组中的短数组length相同
// JSON.stringify(Array.zip([1,2,3],[4,5,6,7,8], function(left, right) { return left + right })) === '[5,7,9]'
Array.zip = function (left, right, combinerFunction) {
let counter,
results = [];
for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
results.push(combinerFunction(left[counter], right[counter]));
}
return results;
};
zip()常用于组合两个数组,返回的数组由输入的两个数组中的元素组成,eg:
let movieLists = [
{
name: "New Releases",
videos: [
{
"id": 70111470,
"title": "Die Hard",
"boxarts": [
{ width: 150, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard150.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/DieHard200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"interestingMoments": [
{ type: "End", time: 213432 },
{ type: "Start", time: 64534 },
{ type: "Middle", time: 323133 }
]
},
{
"id": 654356453,
"title": "Bad Boys",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys200.jpg" },
{ width: 140, height: 200, url: "http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"interestingMoments": [
{ type: "End", time: 54654754 },
{ type: "Start", time: 43524243 },
{ type: "Middle", time: 6575665 }
]
}
]
},
{
name: "Instant Queue",
videos: [
{
"id": 65432445,
"title": "The Chamber",
"boxarts": [
{ width: 130, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg" },
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/TheChamber200.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 4.0,
"interestingMoments": [
{ type: "End", time: 132423 },
{ type: "Start", time: 54637425 },
{ type: "Middle", time: 3452343 }
]
},
{
"id": 675465,
"title": "Fracture",
"boxarts": [
{ width: 200, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture200.jpg" },
{ width: 120, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture120.jpg" },
{ width: 300, height: 200, url: "http://cdn-0.nflximg.com/images/2891/Fracture300.jpg" }
],
"url": "http://api.netflix.com/catalog/titles/movies/70111470",
"rating": 5.0,
"interestingMoments": [
{ type: "End", time: 45632456 },
{ type: "Start", time: 234534 },
{ type: "Middle", time: 3453434 }
]
}
]
}
];
const data = movieLists.concatMap(movieList => {
return movieList.videos.concatMap(video => {
return Array.zip(
video.boxarts.reduceArray((acc, curr) => {
return (acc.width * acc.height < curr.width * curr.height) ? acc : curr;
}),
video.interestingMoments.filter(interestingMoment => {
return interestingMoment.type === "Middle";
}),
(boxart, interestingMoment) => {
return { id: video.id, title: video.title, time: interestingMoment.time, url: boxart.url };
});
});
});
console.log(JSON.stringify(data))
/*
[{"id":70111470,"title":"Die Hard","time":323133,"url":"http://cdn-0.nflximg.com/images/2891/DieHard150.jpg"},
{"id":654356453,"title":"Bad Boys","time":6575665,"url":"http://cdn-0.nflximg.com/images/2891/BadBoys140.jpg"},
{"id":65432445,"title":"The Chamber","time":3452343,"url":"http://cdn-0.nflximg.com/images/2891/TheChamber130.jpg"},
{"id":675465,"title":"Fracture","time":3453434,"url":"http://cdn-0.nflximg.com/images/2891/Fracture120.jpg"}]
*/
总结:
从Rxjs上移植过来的4个操作符,可以很好的扩展数组方法及链式调用,常用场景有:
- 数组的链式调用
- 数组的打平
- 数组转换成Object
- 待发掘…
参考: