A selector is a special form of parameter for the URL. Just like a query string, it is there to change the behaviour of a HTTP request/response based on parameter.
These are mostly implementation dependant but the general convention is to interpret them based on location.
Good simple example will be of tree.json
tree.1.json (1 is the depth selector)
is just another way of saying tree.json?depth=1
similarly tree.2.json is kind of tree.json?depth=2
Semantically, this allows paths which are absolute without a query string and can be cached by dispatcher or CDN (as they don't have ? or # modifiers). Most proxies don't cache parameterised pages (? in the URL) but a selector works around the rule. Also, this allows the servlet to pre-define selector values (if required) and ignore unnecessary parameters.
In light of above, here is direct response to your questions (post edit):
Some of the documents say that it is used for caching response from a page which cannot be done when using query parameters.
Yes, that is true, specially if you put it in the context of reverse proxy like Dispatcher (recommended for publisher in a typical AEM deployment). By default, query string bypass and refresh the cache on a dispatcher. By using a selector, you are able to cache the response on a dispatcher as it does not initiate the cache flush. It is normally a good practice to use selectors if base version of data can be cached and selector depends on the same state.
While some suggest that Selectors are used to respond to different conditions using the same resource. For example we had an implementation where if the page(cq:page) is the last page in the hierarchy, it should display lets say a html-block while if its not the last page (i.e. if it has child pages), it should not display the said block. Here we used a script in the component and added this script name in the URL as selector based on the condition whether it is the last page or not.
Without looking at the code, I am assuming this was something like:
page.last-page.html
where last-page
is the selector you used to differentiate the response. This is one way of doing it and as long as the page can be cached, this selector variant can also be cached without any issues. This is a good use of URL semantics and avoiding caching issues. If this was implemented as:
page.html?last-page=true
it may not have been cache friendly.
Sling has various ways of binding (resolving) a script or servlet to a URL. This resolution is done based on extension, selector(s) and/or path. You can add new functionality to an existing selector or extension by creating new selector based servlets without modifying existing servlets. Consider an example servlet which returns JSON data for a path (we know this exists OOTB in AEM but just an example).
content/mypage.json
returns JSON representation of the node.
Now, let's say you want a new JSON format which is tidier. Using tradition query string, you will do something like
content/mypage.json?tidy=true
This may not be possible if you don't have access to source code of original servlet. So the workaround would be:
content/mypage.tidy.json
This could be a new servlet which accepts the tidy
selector and overrides the function. This makes selectors powerful when it comes to script resolution and code extensions. Yes you can do it with query parameters also but there would be limitations unless you own all the code.