When making requests to an Ethereum node (like those provided by Infura) you have to format your requests in a specific manner - that outlined in the JSON-RPC specification and implemented by Ethereum node clients like Geth and Parity.
Similarly when returning a response web3.js handles lots of the legwork involved in converting the response to a human consumable format.
You can interface with an Ethereum node directly for example by making appropriately formatted CURL requests like those outlined in the Parity documentation but using a library like web3.js saves you from reinventing the wheel and means that developers can focus on their core product.
There are other libraries that do similar things. For example ethers.js.