I could replicate the problem with various shells under FreeBSD, GNU/Linux, and Solaris. It had me head-scratching for more than an hour, so I decided to post the question here
The | is an inter-process communications operator. So, implicitly, there is a subprocess that must be created to parse and evaluate the expression on one side of it or the other. Bash, and older Bourne shells create a subprocess to the right of the operator (reading from the pipe) which means that any variables set there are only in scope until that process exits (which is at the semicolon in this code example.
zsh and newer versions of Korn shell (at least from '93 on, but possibly even as far back as ksh '88) will create the subprocess on the other side of the pipe (writing into it). Thus they will work in the way that the author of this question intends. ("As expected" is, of course, highly subjective. Understanding the nature of the pipe should lead one to expect this to behave in an implementation specific manner).
I don't know if there is any specific provision in Posix or Spec 1170 or SuS or any other published standard that requires one or the other semantics. However, in practice it's clear that you must not depend on the behavior; though you can easily test for it within your scripts.