从源码入手

如同其他常见脚本框架一样,angular的核心代码包裹在一个自调用的匿名函数里边

(function(window, document, undefined) {
    //...
    var angular = window.angular || (window.angular = {});
})(window, document);

在自调用匿名函数内部angular是一个对象,通过publishExternalAPI给angular对象赋值方法,angular.module通过setupModuleLoader赋值。

function publishExternalAPI(angular) {
  extend(angular, {
    'bootstrap': bootstrap,
    'copy': copy,
    //...
  });
  //...
  angularModule = setupModuleLoader(window);
  //...
}

angular.module返回一个module对象,下图是module对象的方法

依赖注入

先看一个实例

<html>

<head>
    <title>AngularJS Dependency Injection</title>
</head>

<body>
    <h2>AngularJS Sample Application</h2>
    <div ng-app="app" ng-controller="CalcController">
        <p>Enter a number:
            <input type="number" ng-model="number" />
            <button ng-click="square()">X<sup>2</sup></button>
            <p>Result: {{result}}</p>
    </div>
    <script src="https://code.angularjs.org/1.5.0/angular.min.js"></script>
    <script>
    var app = angular.module("app", []);

    app.config(function($provide) {
        $provide.provider('MathService', function() {
            this.$get = function() {
                var factory = {};
                factory.multiply = function(a, b) {
                    return a * b;
                }
                return factory;
            };
        });
    });

    app.value("defaultInput", 5);

    app.factory('MathService', function() {
        var factory = {};
        factory.multiply = function(a, b) {
            return a * b;
        }
        return factory;
    });

    app.service('CalcService', function(MathService) {
        this.square = function(a) {
            return MathService.multiply(a, a);
        }
    });

    app.controller('CalcController', function($scope, CalcService, defaultInput) {
        $scope.number = defaultInput;
        $scope.result = CalcService.square($scope.number);

        $scope.square = function() {
            $scope.result = CalcService.square($scope.number);
        }
    });
    </script>
</body>

</html>

config、value、factory、server、controller等方法会向invokeQueue队列中推入相关数组,在document发生DOMContentLoaded事件后启动angularInit。

jqLite(document).ready(function() {
    angularInit(document, bootstrap);
});

jqLite是一个类似jQuery的对象( jQuery、jqLite 与 angular.element)

function angularInit(element, bootstrap) {
    //...
    if (appElement) {
        bootstrap(appElement, module ? [module] : [], config);
    }
    //...
}

angularInit中的bootstrap是一个函数,在angularInit中用来根据ng-app初始化angular应用。bootstrap也可以用来手动启动angular应用,可以通过angular.bootstrap来访问

function bootstrap(element, modules, config) {
    //...
}

手动启动angular应用

<!doctype html>
<html>

<body>
    <div ng-controller="WelcomeController">
        {{greeting}}
    </div>
    <script src="https://code.angularjs.org/1.5.0/angular.min.js"></script>
    <script>
    var app = angular.module('demo', [])
        .controller('WelcomeController', function($scope) {
            $scope.greeting = 'Welcome!';
        });
    angular.bootstrap(document, ['demo']);
    </script>
</body>

</html>

推断式注入

app.controller("myCtrl1", function ($scope, hello1, hello2) {
    $scope.hello = function () {
        hello1.hello();
        hello2.hello();
    }
});

标记式注入

var myCtrl2 = function ($scope, hello1, hello2) {
    $scope.hello = function () {
        hello1.hello();
        hello2.hello();
    }
}
myCtrl2.$injector = ['hello1', 'hello2'];
app.controller("myCtrl2", myCtrl2);

内联式注入

app.controller("myCtrl3", ['$scope', 'hello1', 'hello2', function ($scope, hello1, hello2) {
    $scope.hello = function () {
        hello1.hello();
        hello2.hello();
    }
}]);