$scope
作用域(scope)是构成AngularJS的核心基础,$scope对象是定义应用业务逻辑、控制器方法和视图属性的地方。作用域是视图和控制器之间的胶水,在应用将视图渲染并呈现给用户之前,视图中的模板会和作用域进行连接,然后应用会将对DOM的设置以便将属性变化通知给AngularJS. $scope主要有3个方面的特点:1.通过数据共享连接controller和view;2. 事件的监听和响应; 3. 脏数据检查和数据绑定;
$scope的生命周期
$scope对象的生命周期处理有4个不同的阶段,创建-链接-更新-销毁。
创建
在创建控制器或指令时,AngularJS会用$injector创建一个新的作用域,并在这个新建的控制器或指令运行时将作用域传递出去。
链接
当AngularJS开始运行时,所有的$scope对象都会附加或链接到视图中。所有创建$scope对象的函数也会自身附加到视图中,这些作用域将会注册当AngularJS应用上下文中发生变化时,需要运行的函数。这些函数被称为$watch函数,AngularJS通过这些函数获知何时启动事件循环。
更新
当事件循环运行时,它通常执行在顶层的$scope对象上($rootScope),每个子作用域都执行自己的脏值检测。每个监控函数都会检查变化。如果检查到任意变化,$scope 就会触发指定的回调函数。
销毁
当一个$scope在视图中不再需要的时候,这个作用域将会清理和销毁自己。在$scope对象上调用$destory()方法来清理这个作用域。
AngularJS 1.2 引入了一个叫controller as syntax 的概念。它允许我们直接在控制器内部为当前上下文(this)添加属性,而不需要显式注入scope 对象然后再在上面添加属性。以下代码片段示范了这种简化的语法:
function MainCtrl(){ this.name = "hello world";}MainCtrl.prototype.clicked = function (){ alert("you clicked me");}
Angular 2 更进一步,直接删除了scope 对象。所有表达式都在特定UI 组件的上下文中执行。scope API 整体删掉之后使得Angular 2 得到了大幅度简化,我们不再需要显式注入scope 了,只要把属性直接添加到UI 组件上,然后再进行绑定操作即可。
依赖注入
在JavaScript 领域,AngularJS 1.x 也许是市面上的第一个通过dependencyinjection (DI)引入inversion of control (IoC)机制的框架。DI 可以带来很多好处,比如:易测试性、更好的代码结构和模块化,以及更简洁明了。虽然在1.x 版本中DI 运行得相当不错,但是Angular 2 对它进行了进一步的发挥。因为 Angular 2 是基于最新web 标准构建的,所以它使用了ECMAScript 2016 装饰器(decorator)语法对使用DI的代码进行了注解。这里的装饰器与Python 中的装饰器或Java 中的注解非常类似。它们都可以使用反射机制来decorate(装饰)指定对象的行为。由于装饰器还没有标准化,也不被主流浏览器所支持,所以使用的时候需要经过中间转换步骤。
一个对象通常有三种方式可以获得对其依赖的控制权:
(1) 在内部创建依赖;
(2)通过全局变量进行引用;
(3)在需要的地方通过参数进行传递;
依赖注入是通过第三种方式实现的。从功能上看,依赖注入会事先自动查找依赖关系,并将注入目标告知被依赖的资源,这样可以在目标需要时将资源注入进去。在编写依赖于其他对象或库的组件时,我们需要描述组件之间的依赖关系。在运行期,注入器会创建依赖的实例,并负责将它传递给依赖的消费者。AngularJS使用$injector(注入器服务)来管理依赖关系的查询和实例化。
在运行时,任何模块启动时$injector都会负责实例化,并将需要的所有依赖传递进去。
//使用注入器加载应用var injector = angular.injector(['ng','myApp']);//通过注入器加载$controller服务var $controller = injector.get('$controller');var scope = injector.get('$rootScope').$new();//加载控制器并传入一个作用域,同angularJS在运行时做的一样var myController = $controller('MyController',{$scope: scope});
在任何一个AngularJS应用中,都有$injector在进行工作,无论我们知道与否。当编写控制器是,如果没有使用[]标记或进行显示的声明,$injector就会尝试通过参数名腿短依赖关系。
推断式注入声明
如果函数没有明确的声明,AngularJS会假定参数名称就是依赖的名称。它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,通过$injector将这些参数注入进对象实例。
injector.invoke(function($http,greeter){});
注意!!!:JavaScript的压缩器通常会将参数名改写成简单的字符,以减小源文件体积(同时也会删除空格、空行和注释等),如果我们不明确地描述依赖关系,AngularJS将无法根据参数名称推断出实际的依赖关系,也无法进行依赖注入。注入过程只适用于未经过压缩和混淆的代码,因为AngularJS需要原始为经压缩的参数列表来解析。
显式注入声明
AngularJS提供显示方法来定义一个函数在被调用时需要用到的依赖关系。显示声明不受源代码被压缩和混淆的情况影响,依然能正常工作。通过$injector属性来实现显示注入声明的功能。函数对象的$injector属性是一个数组,数组元素的类型时字符串,它们的值就是需要被注入的服务名称。
var controllerFactory = function controller($scope, greeter){ //控制器};controllerFactory.$inject = ['$scope','greeter'];//应用控制器angular.module('myApp',[]).controller('myController', controllerFactory).factory('greeter',greeterService);//获取注入器并创建一个新的作用域var injector = angular.injector(['ng','myApp']), controller = injector.get('$controller'), rootScope = injector.get('$rootScope'), newScope = rootScope.$new();//调用控制器controller('MyController', {$scope: newScope});
注意!!!: 显示声明,参数顺序非常重要,因为$injector数组元素的顺序必须和注入参数顺序一一对应,这种方式可以在压缩后的代码中运行。
行内注入声明
我们在函数定义时从行内将参数传入,是AngularJS提供的行内注入声明,它避免在定义过程中使用临时变量。在定义一个AngularJS的对象时,行内声明的方式允许我们直接传入一个参数数组而不是一个函数。数组的元素时字符串,它们代表的是可以被注入到对象中的依赖的名字,最后一个参数就是依赖注入的目标函数对象本身。
angular.module('myApp').controller('myController',['$scope','greeter',function ($scope, greeter){}]);
注意!!!:行内注入声明可以在压缩后的代码中正常运行。
参考资料:
【美】Ari Lerner 著《AngularJS 权威教程》
转载时请注明: