$scope vs scope

If you've gone through our directives tutorial, you may be wondering what the difference is between the scope variable used in the directive linking function and the $scope variable used in controller is.

To illustrate this difference, we'll start with some sample code:

<html>
    <head>
  <title>$scope vs scope</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0-beta.5/angular.min.js"></script>
        <script src="app.js"></script>
    </head>
    <body ng-app="app">
    <div ng-controller="TestCtrl">
      <test-directive></test-directive>
    </div>
    </body>
</html>
angular.module('app', [])
.controller('TestCtrl', function($scope) {
  console.log($scope)
})
.directive('testDirective', function() {
  return {
    link: function(scope) {
      console.log(scope)
    }
  }
})

If you open your developer console and refresh the page, you should see both $scope and scope. Notice that they both have a $id property with the same value, which means that these two variables actually refer to the exact same scope object.

Now let's modify our directive to use an isolated scope:

.directive('testDirective', function() {
  return {
    scope: {},
    link: function(scope) {
      console.log(scope)
    }
  }
})

If we refresh the page, the two $ids will be different. This is because the directive has created a brand new scope object.

{info} It's important to realize that regardless of name both scope and $scope objects are instances of the Scope class. The various properties and methods of this class are documented under $rootScope.Scope

You may be wondering why a scope object is referred to by scope in linking functions and $scope in controllers. The answer lies with Angular's dependency injections system.

A directive's link function is just a normal JavaScript function that has a type signature of scope, element, attributes object in that order. If were to flip around the arguments and declare our link function like:

.directive('testDirective', function() {
  return {
    scope: {},
    link: function(attr, elem, scope) {
      console.log(scope)
    }
  }
})

The first parameter (now called attr) would STILL be the scope object.

Unlike link functions, controller functions are called by Angular's injection system. This system is more intelligent then a basic function call and will actually inspect the function parameter names and pass the appropriate values to the controller function.

Let's see what happens if we try to create a controller using scope instead of $scope:

.controller('TestCtrl', function(scope) {
  console.log(scope)
})

We are greeted with an error message: Error: [$injector:unpr] Unknown provider: scopeProvider <- scope <- TestCtrl. Angular's injection system attempts to locate the dependency called scope and is unable to find it so it throws an error.

A result of this injection system is that the order dependencies are declared does not matter as long as the dependencies are able to be resolved. Say we wanted to make an XHR requests to a server using Angular's $http service. We could declare the controller in either of the following ways:

.controller('TestCtrl', function($scope, $http) {
  console.log($scope)
  console.log($http)
})
.controller('TestCtrl', function($http, $scope) {
  console.log($scope)
  console.log($http)
})

As we found out above, the name of a dependency matters however a common practice is to minify JavaScript files and mangle variable names before deploying an app to production. Prevent your Angular code from breaking, it is important to add dependency annotations to your code.

To learn more, checkout our guide to explicit dependency injections with annotations.

You should also check out our guide on using ng-annotate to automate dependency annotations.