map() 本身是运算符吗?还是算子是map()的返回值?
map() 的当前实现如下所示:
export function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R> {
return function mapOperation(source: Observable<T>): Observable<R> {
if (typeof project !== 'function') {
throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
}
return source.lift(new MapOperator(project, thisArg));
};
}
所以,map() 是一个函数。它是 RxJS 术语中的运算符,是的,但它仍然是一个常规的 JavaScript 函数。就是这样。
此运算符接收projection 回调函数,该回调函数由map 运算符调用。这个回调是你传递给map()的东西,例如value => value.id 来自这个例子:
source$.pipe(map(value => value.id))
map的返回值也是一个函数(声明为OperatorFunction<T, R>)。从mapreturnsfunction mapOperation()开始,你可以看出它是一个函数。
现在,mapOperation 函数只接收一个参数:source,它的类型为 Observable<T>,并返回另一个(转换后的)Observable<R>。
总结一下,当你说:
Pipeable Operator 是一个将 Observable 作为其输入并返回另一个 Observable 的函数。
这意味着当 RxJS 运算符(它是一个函数)将 Observable 作为其输入并返回另一个 Observable 时,它是可管道化的,在我们的例子中是这样:map 运算符确实返回一个函数 (mapOperation),其签名 ((source: Observable<T>): Observable<R>) 准确地表明:它接受一个 Observable 并返回另一个。
运算符是一个函数,它将一个可观察对象(源)作为其第一个参数并返回另一个可观察对象(目标,或外部可观察对象)
我已经多次提到运算符是一个公正的函数。
操作员接受配置选项,并返回一个接受源可观察的函数。
是的,在这种情况下,map() 可以被称为运算符,因为它接收配置选项 - projecttion 回调函数。所以,这里真的没有冲突,因为许多操作符都是可配置的。
我会说这个有冲突:
运算符应始终返回 Observable [..] 如果您创建的方法返回 Observable 以外的内容,则它不是运算符,这很好。
我猜这是当可管道操作符不可管道时的旧定义。在引入 pipeable 操作符之前(我认为是在 RxJS 的第 5 版中),操作符返回的是 Observables。一个旧的map() 实现就表明了这一点。
有关 RxJS 的创建者为何决定引入可管道操作符的更多信息,请查看this document。
关于什么是 Observables 的另一篇精彩文章可以在 here 找到。
还有:
Creation Operators 是另一种运算符,可以作为独立函数调用以创建新的 Observable。
of() 是一个创建运算符的示例,它返回(创建)一个 Observable。请看源代码。
TL;DR:map() 是一个通常有一个参数(投影回调函数)的函数,它还返回一个函数,该函数接收源 Observable 并返回目标 Observable。
编辑:为了回答您来自 cmets 的问题,我想在这里做。
是的,在 RxJS 6 中,您可以创建一个函数来接受 observable 并返回另一个,这就是运算符。例如
function myOperatorFunction(s: Observable<any>) {
return of(typeof s);
}
你会这样称呼它
source$.pipe(myOperatorFunction);
请注意,我没有在pipe() 中调用myOperatorFunction,我只是传递了对它的引用,即我没有用括号写myOperatorFunction,但没有括号。那是因为pipe 接收函数。
如果您需要传递一些数据或回调函数,例如 map 示例,您必须有另一个函数来接收您的参数,就像 map 接收 projection 参数一样,并且随心所欲地使用它。
现在,您可能想知道为什么有些运算符不接收任何数据,但仍被创建为返回函数的函数,例如refCount()。这是为了与其他大多数具有一些参数的运算符相吻合,因此您不必记住哪些不接收参数或哪些具有默认参数(如min())。如果是refCount,如果写的和现在有点不同,你可以写
source$.pipe(refCountOperatorFunction);
而不是
source$.pipe(refCount());
但是你必须知道你必须这样写,所以这就是为什么函数被用来返回函数(接收可观察对象并返回另一个可观察对象)的原因。
编辑 2:现在您知道内置运算符返回函数,您可以通过传入可观察的源代码调用它们。例如
map(value => value.toString())(of())
但这是丑陋的,不推荐管道操作员的方式,尽管它仍然可以工作。让我们看看它的实际效果:
of(1, 2, true, false, {a: 'b'})
.pipe(
map(value => value.toString()),
filter(value => value.endsWith('e'))
).subscribe(value => console.log(value));
也可以这样写:
filter((value: string) => value.endsWith('e'))(map(value => value.toString())(of(1, 2, true, false, {a: 'b'})))
.subscribe(a => console.log(a));
虽然这是一个完全有效的 RxJS 代码,但当您阅读后一个示例时,您无法想到它的作用。 pipe 在这里实际上做的是它 reduces over 传入的所有函数,并通过将之前的源 Observable 传递给当前函数来调用它们。