“Rxjs中的’Tap操作符的处理程序中,您可以对对象进行变异’的含义是什么?”

huangapple go评论96阅读模式
英文:

Rxjs what is the meaning of "You can mutate objects as they pass through the tap operator's handlers." in Tap operator

问题

在链接点击中提到:

"小心!当对象通过tap操作符的处理程序时,您可以突变对象。"

这是什么意思?

我尝试了以下测试:

obs3 = new Observable<string>((observer) => {
  console.log('Observable 3 starts')
  observer.next('1');
  observer.next('2');
  observer.next('3');
  observer.next('4');
  observer.next('5');
})

ngOnInit() {             
   this.obs3.pipe(tap(val => val+1)).subscribe(
        val => {console.log(val);}
    );
}

控制台输出是:

Observable 3 starts
1
2
3
4
5

那么,tap返回的Observable与源Observable是完全一样的,那么"小心!当对象通过tap操作符的处理程序时,您可以突变对象。"这句话的意思是什么?

*************[2023年7月7日编辑] *******************************

根据Ruth的答案,我尝试比较tap之前的输入Observable和tap之后的输出Observable是否相等,通过===,它们不相等。

const inputObj = {
          a: 'test obj prop',
          b: 5,
          c: ['x', 'y', 'z']
        };
 of(inputObj) === of(inputObj)
        .pipe(
          tap((input) => {
            input.a = 'changed prop value';
            input.b = 60;
            input.c = ['1', '2', '3'];              
            //return input;
          })
        )? console.log('after tap:still equal'):console.log('after tap: Not equal');

输出是:

after tap: Not equal

所以这变成了另一个问题,它违反了关于tap返回的Observable是源的完全镜像的API。

英文:

in the link tap, it mentions

&quot;Be careful! You can mutate objects as they pass through the tap operator&#39;s handlers.&quot;

What is the meaning of this?

I try the following test

obs3 = new Observable&lt;string&gt;((observer) =&gt; {
      console.log(&#39;Observable 3 starts&#39;)
      observer.next(&#39;1&#39;);
      observer.next(&#39;2&#39;);
      observer.next(&#39;3&#39;);          
      observer.next(&#39;4&#39;);
      observer.next(&#39;5&#39;);
      })

ngOnInit() {             
       this.obs3.pipe(tap(val=&gt;val+1)).subscribe(
            val=&gt;{console.log(val);}
        );
}

the console output is

Observable 3 starts
1
2
3
4
5

so The observable returned by tap is an exact mirror of the source, but then what is the meaning of the statement

> Be careful! You can mutate objects as they pass through the tap operator's handlers.

?

*************[Edit on 20230707] *******************************

respond with Ruth answer, I try to compare the input observable before tap and output observable after tap by ===, and they are not equals.

const inputObj = {
          a: &#39;test obj prop&#39;,
          b: 5,
          c: [&#39;x&#39;, &#39;y&#39;, &#39;z&#39;]
        };
 of(inputObj) === of(inputObj)
        .pipe(
          tap((input) =&gt; {
            input.a = &#39;changed prop value&#39;;
            input.b = 60;
            input.c = [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;];              
            //return input;
          })
        )? console.log(&#39;after tap:still equal&#39;):console.log(&#39;after tap: Not equal&#39;);

the output is

after tap: Not equal

so this becomes another problem, it violates the API about

> The observable returned by tap is an exact mirror of the source

答案1

得分: 2

除了@Picci的出色解释外,我想补充一下为什么你的输入没有改变。

这是因为你传递的是数字,而不是在文档中提到的_对象_给tap。数字是原始类型,根据定义,原始类型是不可变的。

然而,当你传递一个对象时,你可以看到它在tap内部如何被改变:

const { of } = rxjs;
const { tap } = rxjs.operators;

const inputObj = {
  a: 'test obj prop',
  b: 5,
  c: ['x', 'y', 'z']
};

console.log('Before `tap`:', inputObj);

of(inputObj)
  .pipe(
    tap((input) => {
      input.a = 'changed prop value';
      input.b = 'number -> string change';
      input.c = [1, 2, 3];
      
      return input;
    })
  )
  .subscribe(() => 
    console.log('After `tap`:', inputObj)
  );
英文:

In addition to the excellent explanation by @Picci, I'd like to add why your input didn't change.

It's because you passed numbers, not objects, as mentioned in the docs, to the tap. Numbers are primitives, and by definition primitives are immutable.

However, when you pass an object, you could see how it could be mutated inside the tap:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const { of } = rxjs;
const { tap } = rxjs.operators;

const inputObj = {
  a: &#39;test obj prop&#39;,
  b: 5,
  c: [&#39;x&#39;, &#39;y&#39;, &#39;z&#39;]
};

console.log(&#39;Before `tap`:&#39;, inputObj);

of(inputObj)
  .pipe(
    tap((input) =&gt; {
      input.a = &#39;changed prop value&#39;;
      input.b = &#39;number -&gt; string change&#39;;
      input.c = [1, 2, 3];
      
      return input;
    })
  )
  .subscribe(() =&gt; 
    console.log(&#39;After `tap`:&#39;, inputObj)
  );

<!-- language: lang-css -->

.as-console-wrapper { max-height: 100% !important; top: 0; }

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

答案2

得分: 1

rxjs遵循一种函数式方法。这意味着它鼓励使用“纯函数”。 “纯函数”应遵循不可变性概念,即应接收输入,处理该输入而不更改它,并返回输出。它们不应具有“副作用”,即不应更改函数范围之外的任何内容。这些概念应提高代码的可读性、可理解性和可测试性。

同时,没有副作用的程序基本上是无用的。因此,通常在纯函数处理的边缘有一个地方,副作用会发生。

“tap”操作符是用于在rxjs中实现“副作用”的操作符。在实际情况下,tap接收一个输入值,执行操作,然后返回接收到的相同输入值。如果接收到的输入值是一个对象,那么该对象可以发生变化。因此,你在文档中读到的警告。

在你的情况下,你没有看到任何变化,因为你所做的不是“更改输入”(它是一个数字,因此不能被更改)。你输入的数字与tap返回的相同。

还有一个概念:观察者

tap期望的输入是一个Observer。一个Observer是一个类型为

interface Observer<T> {
  next: (value: T) => void
  error: (err: any) => void
  complete: () => void
}

的对象,这正是subscribe函数期望的相同输入。因此,你可以想象tap是放置在pipe操作符链中间的一种订阅。

你使用tapsubscribe的方式是一种简化的方式,只传递一个函数给它们,这意味着你只传递了next函数,忽略了errorcomplete

英文:

rxjs follows a functional approach. Which means that it encourages the use of "pure functions". "pure functions" should follow the immutability concept, i.e. should receive input, elaborate that input without changing it and return the output. They should not have "side effects", i.e. they should not change anything outside the scope of the function. These concepts should enhance readability, understandability and testability of sw.

At the same time, programs without side effects are basically useless. So there must be a place, usually at the margin of pure function processing, where side effects take place.

The tap operator is the operator designed to implement "side effects" in rxjs. In practical terms tap receives an input value, does whatever, and then returns that same input value received. If the input value received is an object, that object can be mutated. Hence the warning you read in the documentation.

In you case you do not see any mutation since what you do is not "change the input" (which is a number and therefore can not be changed). The number you get in input is the same one tap returns.

One more concept: Observers.

What tap expects in input is an Observer. An Observer is an object of type

interface Observer&lt;T&gt; {
  next: (value: T) =&gt; void
  error: (err: any) =&gt; void
  complete: () =&gt; void
}

which is exactly the same input expected by the function subscribe. Hence you can imagine that tap is a sort of subscription placed in the middle of a pipe of operators.

The way you have used tap and subscribe passing to them just one function is a simplified way of using tap and subscribe and means that you are just passing the next function, ignoring error and complete.

答案3

得分: 0

这与接收对象作为参数的任何其他函数一样,都会收到相同的警告,因为这些参数是作为引用传递而不是作为值传递的。

我们可以设计我们自己的 tap 函数,它会受到相同的影响:

const tap = fn => x => (fn(x), x);

const obj = {foo: 'bar'};

( tap(x => console.log(x.foo = 42)) //<- mutate
  )(obj);

console.log(obj); //<- mutated! not {foo:'bar'} anymore
英文:

This is the same warning as for any other functions receiving objects as arguments as those are passed as references and not as values.

We can design our own tap function which would be affected in the same way:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const tap = fn =&gt; x =&gt; (fn(x), x);

const obj = {foo: &#39;bar&#39;};

( tap(x =&gt; console.log(x.foo = 42)) //&lt;- mutate
  )(obj);

console.log(obj); //&lt;- mutated! not {foo:&#39;bar&#39;} anymore

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年7月6日 13:04:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76625648.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定