Both the APIs have the 'readyState' property that indicates the status of the connection at a given time. The time duration for which a specific readyState value lasts has been found to vary based on the status of the target port to which the connection is being made. This means that by observing this difference in behavior we can determine if the port being connected to is open, closed or filtered. For Cross Origin Requests it is the duration of readyState 1 and for WebSockets it is readyState 0.
I tried to do some calibration of the time duration for the different port states and the data is below. These numbers only hold good when the target is in the internal network. If you are scanning a target on the internet then the network latency should be taken in to account.
Since this is not a socket-level but an application-level scan the success also depends on the nature of the application running on the target ports. When a request is sent to certain type of applications they read the request and remain silent keeping the socket open, probably expecting more input or input in the format they expect. If the target is running such a application then its status cannot be determined.
Since even closed ports can be identified we can extend this technique to perform network scanning as well as internal IP detection. I have written a tool called JS-Recon which can perform these. More details on the how JS-Recon works is here. These techniques only work when run from Windows machines, on *nix systems it is not possible to determine closed ports and the timing figures are quite different.