To better understand let's apply the three expressions plus a capturing group and analyse each behaviour.
() capturing group - the regex inside the parenthesis must be matched and the match create a capturing group
(?:) non-capturing group - the regex inside the parenthesis must be matched but does not create the capturing group
(?=) positive lookahead - asserts that the regex must be matched
(?!) negative lookahead - asserts that it is impossible to match the regex
Let's apply q(u)i to quit.
q matches q and the capturing group u matches u.
The match inside the capturing group is taken and a capturing group is created.
So the engine continues with i.
And i will match i.
This last match attempt is successful.
qui is matched and a capturing group with u is created.
Let's apply q(?:u)i to quit.
Again, q matches q and the non-capturing group u matches u.
The match from the non-capturing group is taken, but the capturing group is not created.
So the engine continues with i.
And i will match i.
This last match attempt is successful.
qui is matched.
Let's apply q(?=u)i to quit.
The lookahead is positive and is followed by another token.
Again, q matches q and u matches u.
But the match from the lookahead must be discarded, so the engine steps back from i in the string to u.
Given that the lookahead was successful the engine continues with i.
But i cannot match u.
So this match attempt fails.
Let's apply q(?=u)u to quit.
The lookahead is positive and is followed by another token.
Again, q matches q and u matches u.
But the match from the lookahead must be discarded, so the engine steps back from u in the string to u.
Given that the lookahead was successful the engine continues with u.
And u will match u. So this match attempt is successful.
qu is matched.
Let's apply q(?!i)u to quit.
Even in this case lookahead is positive (because i does not match) and is followed by another token.
Again, q matches q and i doesn't match u.
The match from the lookahead must be discarded, so the engine steps back from u in the string to u.
Given that the lookahead was successful the engine continues with u.
And u will match u.
So this match attempt is successful.
qu is matched.
So, in conclusion, the real difference between lookahead and non-capturing groups is all about if you want just to test the existence or test and save the match.
But capturing groups are expensive so use it judiciously.