$scope isn't actually a service !
This is why you don't have to pass it as a dependency of the directive. $scope is a local passed to the injector, a value passed to the function, as if i would do console.log(myValue). myValue is not a service.
The $injector's invoke method, which passes services to a function, can replace a requested dependency by a given value, see the docs for more details. I recognize that mixing services and simples values in the arguments list are confusing, as we can't know which one are the services and which one are simple arguments, but it works that way.
We can consider that something like this is executed when a new controller is instanciated :
var $rootScope = $injector.get('$rootScope');
$injector.invoke(controller, context /*the this binding*/, {
$scope: $rootScope.new()
});
See also the code of the controller service for further details.
Update : difference between $scope and services
Take this function :
function add(a, b) {
return a + b;
}
a and b are "simple arguments", they are the values on which the function perform calculations. Imagine that you want to broadcast a message to your app that an addition just have been performed. In Angular, we can use the $rootScope.$broadcast method.
function add(a, b) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
But here, $rootScope has never been declared. We have to load the $rootScope service in our function. Angular uses the $injector.invoke method in order to do this : it takes a function or an array (the "square bracket" notation), extract the services names from the function arguments/array elements, and calls the function with the corresponding services passed as arguments.
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']); // Creates the injector, with the services of `ng` module.
$injector.invoke(add); // Calls the function with the requested services
It will throw an error, because a and b aren't services. In fact, they don't have to be services, because they are values that we want to set ourself. For the injector, they are locals. In order to perform the addition of 2 and 3 with the add function, we have to do the following :
function add(a, b, $rootScope) {
$rootScope.$broadcast('addition.done', a + b);
return a + b;
}
var $injector = angular.injector(['ng']);
$injector.invoke(add, this, {
a: 2, // When requesting the `a` service, return the value `2`
b: 3 // The same here, with `b` and `3`
});
The same is done with controllers. $scope itself is not a service, but a new scope, build each time the directive is loaded. Like a and b, the newly created $scope is a value on which the function perform some logic and modifications. $scope is not a dependency, but an argument of the function. And because the $injector extracts the dependencies from the arguments list, we can't tell if an argument is a service or not (except if you already know it, but this isn't an evidence). Here, $scope is not a service, and is also the same object that in the link function in your directive. Note the fact that the documentation does not name the scope argument $scope, but scope. If scope was a service, it would throw an error, but actually it doesn't, because the link function isn't called with $injector.invoke.