VLOOKUP and its problems
=VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])
Three real problems. It can only look rightward from the key column. The col_index_num is a hard-coded number, so inserting a column silently breaks it. And the last argument defaults to TRUE (approximate match) if omitted, which produces wrong answers on unsorted data — always pass FALSE explicitly.
INDEX/MATCH
=INDEX(return_range, MATCH(lookup_value, lookup_range, 0))
MATCH finds the position; INDEX returns the value at that position. It looks in any direction, does not break when columns are inserted, and the 0 forces exact match.
This was the professional's answer for two decades and still works in every version of Excel.
XLOOKUP
=XLOOKUP(lookup_value, lookup_array, return_array, [if_not_found], [match_mode], [search_mode])
=XLOOKUP(A2, Products[SKU], Products[Price], "Not found")
Exact match is the default. It searches any direction. The not-found argument replaces IFNA wrapping. It can return an entire row or column as a spilled array. And search_mode of -1 searches from the bottom, which makes finding the most recent matching record trivial.
It requires Microsoft 365 or Excel 2021 — that is the only reason not to use it.
Two-way lookup
To find a value at the intersection of a row and column:
=XLOOKUP(A2, Table[Product], XLOOKUP(B1, Table[[#Headers],[Jan]:[Dec]], Table[Jan]:Table[Dec]))
The classic equivalent is =INDEX(range, MATCH(row_val, rows, 0), MATCH(col_val, cols, 0)), which is often the clearer way to write it.