关于AngularJS1中的Scope体系


AngularJS会在程序启动时创建一个$rootScope实例,然后再去搜索HTML DOM 树代码中
如果遇见ng- 元素,它就会为它们调用Scope.$new()方法来创建一个子 $scope,
而且每个子Scope内部都有一个$parent 属性指向其上一级Scope对象。

如此一个DOM树中会有很多范围创建指令,所以会有很多scope被创建出来。
它们都是以$rootScope为根的树形结构关系。它也是一个小型化了的DOM结构。

var WorldCtrl = function ($scope) {
    $scope.population = 7000;
    $scope.countries = [
        {name: 'France', population: 63.1},
        {name: 'United Kingdom', population: 61.8},
    ];
};

<ul ng-controller="WorldCtrl">
    <li ng-repeat="country in countries">
        {{country.name}} has population of {{country.population}}
    </li>
    <hr>
    World's population: {{population}} millions
</ul>

这里我们创建了一个新变量country,把它暴露在$scope 里一边在页面上绑定。
但是由于重复创建会覆盖之前的,所以Angluar采取了为集合中的每一个元素都创建一个scope
新创建的Scope会完全匹配DOM树结构。

不同的DOM元素对应着不同的Scope对象,使用变量从相应的Scope中渲染模板。
上例中每个<li>元素会对应一个自己的scope,可以在它里面定义。

如此我们可以给WorldCtrl管理的scope定义一个worldsPercentage函数
$scope.worldsPercentage = function (countryPopulation) {
    return (countryPopulation / $scope.population)*100;
}
我们就可以在ng-repeat指令里随便使用了,因为它们都是它的子集。
<li ng-repeat="country in countries">
    {{country.name}} has population of {{country.population}},
    {{worldsPercentage(country.population)}} % of the World's population
</li>

Scope的继承采用原型继承方式,当我们读取一个属性时,它会沿着继承链逐层向上寻找 。


var HelloCtrl = function ($scope) {
};

<body ng-app ng-init="name='World'">
<h1>Hello, {{name}}</h1>
<div ng-controller="HelloCtrl">
Say hello to: <input type="text" ng-model="name">
<h2>Hello, {{name}}!</h2>
</div>
</body>

我们可以使用$parent.propertyName 来显式引用父Scope中定义的变量。
<input type="text" ng-model="$parent.name">
这种做法是建立在DOM树结构不变的基础上的,但如果在<input>元素上层插入另外一个DOM
元素时,显然$parent就发生的变化,所以尽量避免这种显式的使用方法。

另外的一种解决办法是将属性绑定到一个对象,而不是直接绑定到scope的属性。

<body ng-app ng-init="thing={name:'World'}">
<h1>Hello,{{thing.name}}</h1>
<div ng-controller="HelloCtrl">
    Say hello to:<input type="Text' ng-model="thing.name">
    <h2>Hello,{{thing.name}}!</h2>
</div>
</body>
如此只是在应用程序范围内定义了一个对象thing,所以各个级别的Scope都能访问到。

Scope层级和事件系统:
Scope的层级体系可以用作事件总线。
AngularJS允许我们在Scope层级体系内传递命名事件和其负载。
事件可以从任何scope中被发出,分为上行$emit或者下行$broadcast在Scope层级体系中进行。

AngularJS核心服务和指令使用该事件总线来标识应用程序状态的重要变化。
比如我们可以监听从$rootScope发出的$locationChangeSuccess事件,来当浏览器中的URL发生变化采取相应的处理。
$scope.$on('$locationChangeSuccess',function(event,newUrl,oldUrl){
});

$on()方法可以在任意scope实例上调用,用于注册一个scope事件处理器。
该处理器函数将把event对象作为第一个参数,后续的参数将跟事件的负载和事件类型相关。

跟DOM事件类似,我们在一个event对象上调用 preventDefault()和stopPropagation()方法
将会阻止事件继续在scope层级体系内传播。
其中stopPropagation() 用于从下向上的$emit产生事件。

事件在scope层级体系内传递将会为许多问题带来极好的解决方案。
尤其是有关全局,异步的状态发生变化时通知某些元素。不过,这种方式现实中使用较少。
通常我们使用的双向数据绑定来解决。
在整个AngularJS框架中,只有三个事件被发射
$includeContentRequested
$includeContentLoaded
$viewContentLoaded
和七个事件被广播:
$locationChangeStart
$locationChangeSuccess
$routeUpdate
$routeChangeStart
$routeChangeSuccess
$routeChangeError
$destroy

优先使用数据双向绑定方案。

Scopes的生命周期
Scopes用于提高隔离的空间避免变量名冲突。
Scopes用于分层组织管理内存空间。

当一个 scope不再需要时就会被销毁,在其内定义的model和功能函数都将一并被垃圾收集器收回。

新的scope一般都是被scope的创建指令负责创建和销毁。
也可以通过手动调用scope的$new()和$destroy()方法来完成创建和销毁。

关于视图:
AngularJS使用HTML语言以及增强HTML元素来定义页面。
AngularJS在浏览器将标记文本转化为DOM树后切入并沿着DOM结构逐步解析。
只要遇到它的指令,AngularJS就会执行它自己的逻辑将指令转化成动态部分展示。

Angular依托于合法的DOM树,它需要跟HTML的DOM树 类似于创建一个domain-specific language(DSL) 
来告诉浏览器如何处理新的增强元素。


AngularJS本身定义了一个顶级的命名空间angular,它下面定义了很多方便的属性和函数。
module() 就是用来定义一个模块空间的函数。
module 用作管理Angular其它对象的容器,它管理诸如controllers,services等)
angular.module('moduleName',[dependencyList]) 返回一个module对象。

定义完成module后,我们还需要把它放置到DOM结构中,通知AngularJS它的存在,否则它没有运行的入口。
<body ng-app="hello">

对象协作:
AnglarJS提供了module来组织管理相互协作的对象,它不仅仅可以注册框架可以直接调用的对象(controllers,filters等)
还可以注册开发人员定义的任意对象。

Module模式在我们组织应用程序代码时非常有用,AngularJS对其的利用更进一步。
出了注册对象到一个命名空间外,它还能通过声明描述这些对象之间的依赖关系。

var NotificationsService = function () {
this.MAX_LEN = 10;
this.notificationsArchive = new NotificationsArchive();
this.notifications = [];
};
NotificationsService.prototype.push = function (notification) {
var newLen, notificationToArchive;
newLen = this.notifications.unshift(notification);
if (newLen > this.MAX_LEN) {
    notificationToArchive = this.notifications.pop();
        this.notificationsArchive.archive(notificationToArchive);
    }
    };
    NotificationsService.prototype.getCurrent = function () {
    return this.notifications;
};

服务注册
AngularJS中有一个专门的服务$provide 用于注册不同的对象创建方法。
注册的方法会被$injector 截取器截取后提供对象实例。

$injector服务创建具体的对象,整个生命周期过程中Angular只会截取创建方法一次
创建一个单独的对象实例。
也就是所$injector创建的对象实例是单例模式的,每一个运行的应用程序只能有一个。

AngularJS的Module只是保持这些对象实例,这些对象实例的创建是由我们来控制的。

最简单的事Value方法:
var myMod = angular.module('myMod', []);
myMod.value('notificationsArchive', new NotificationsArchive());
使用该方法注册的对象,一般不会依赖其它对象。属于简单对象。

Value()注册无依赖的简单对象。

Service方法:
当我们需要的对象有依赖时,我们就不能用value方法定义了。
最简单的注册一个有其它对象依赖的对象的方法是service(),它用来注册一个构造函数。
myMod.service('notificationsService',NotificationsService);
var NotificationsService = function (notificationsArchive) {
    this.notificationsArchive = notificationsArchive;
};

Service 方法注册有依赖的对象构造函数。

注意:在AngularJS世界里,service既可以是注册构造函数或者由AngularJS DI管理的任意单例对象的方法。

实际中,Service不太常用,主要是注册已有的构造函数,让AngularJS可以使用这些构造函数创建对象。


Factory()
它是另一个为对象创建注册方法的函数。
它比Service()方法更加灵活一些,因为我们可以注册任意的对象创建函数,而不一定非是构造函数。

myMod.factory('notificationsService',function(notificationsArchive){
    var MAX_LEN = 10;
    var notifications = [];
    return {
        push:function (notification) {
        var notificationToArchive;
        var newLen = notifications.unshift(notification);
        //push method can rely on the closure scope now!
        if (newLen > MAX_LEN) {
        notificationToArchive = this.notifications.pop();
        notificationsArchive.archive(notificationToArchive);
        }
        },
        // other methods of the NotificationsService
    };

AngularJS使用factory函数来注册返回对象,它可以是任意的JS对象,包括function对象。

该方法是AngularJS依赖系统中最为常用的获取对象的方式。
它非常灵活,可以包含非常精致的对象创建逻辑。
它是标准函数,我们可以使用private等范围限制变量,对一些内部逻辑进行封装。

Constant()
在一个模块范围内定义常量并注入到其它合作对象时,使用该方法。
myMod.constant('MAX_LEN', 10);
它在创建服务时非常有用,能够跨多应用程序共享。唯一问题时,它要求在定义时必须设置初始值。

Provider()
是上面介绍的所有方法的通用版本。
它被定义成一个函数,该函数必须返回一个包含$get属性的对象。
而$get属性是一个工厂函数,它被调用时会返回一个service实例。

我们可以把provider认为是一个嵌入了一个工厂方法的对象。
Provider方法返回一个对象,该对象可以包含额外的属性和方法,用于在$get()方法被调用前
设置配置可选项值,以及更加复杂的配置逻辑。

模块的生命周期:
AngularJS支持多种对象的创建方式。其中Provider是最为特别的,它在生产对象实例时支持更加
深入的配置设置。
为了支持provider,AngularJS 将module的生命周期划分为两个阶段。
配置阶段和运行阶段
配置阶段主要完成所有配方方法的调用和配置
运行阶段则主要是实例化后的一些逻辑执行。

myMod.config(function(notificationsServiceProvider){
    notificationsServiceProvider.setMaxLen(5);
});
而运行阶段,我们可以认为是应用程序的主函数,与其它语言不同的是AngularJS
模块可以有多个配置和运行块。
一个运行的应用程序其实是一些相互协作的对象集合。
它可以有多个入口。

angular.module('upTimeApp', []).run(function($rootScope) {
    $rootScope.appStarted = new Date();
});


两个阶段的不同注册方法:
Variable,Service,Factory在配置阶段不能注册
而 Constant和Provider是可以在配置阶段注册的
运行阶段能够注册的方法有Constant,Variable,Service,Factory等。
注意Provider不能在运行阶段注册。


模块之间的依赖关系:我们可以将相关的服务放到一个module里,创建可以重用的service类库。
angular.module('application', ['notifications', 'archive'])

顶层的module可以声明依赖它所需要的所有模块。
Services可以依赖其它services,values,和constants

服务和它们的跨模块可见性:
我们可以在某个字模块里定义一个service然后将它注入到父模块中使用。
angular.module('app', ['engines'])
    .factory('car', function ($log, dieselEngine) {
    return {
        start: function() {
            $log.info('Starting ' + dieselEngine.type);
        };
    }
});
angular.module('engines', [])
    .factory('dieselEngine', function () {
        return {
            type: 'diesel'
        };
});

兄弟模块定义的内容,相互之间也是可见的。
angular.module('app', ['engines', 'cars'])

angular.module('cars', [])
    .factory('car', function ($log, dieselEngine) {
        return {
            start: function() {
                $log.info('Starting ' + dieselEngine.type);
            }
    };
});
angular.module('engines', [])
    .factory('dieselEngine', function () {
        return {
            type: 'diesel'
        };
});

在一个应用程序模块中定义的服务在其它所有模块中都是可见的,也就是说模块的层级不会
影响所有定义服务的可见性。
在应用程序启动时,AngularJS会将所有模块中的服务定义都集中到一个应用程序的全局空间内。
所以,所有服务必须是单例模式的。

我们在使用依赖对象而又不想使用其某个方法定义时,可以直接重新定义该方法。
angular.module('app', ['engines', 'cars'])
    .controller('AppCtrl', function ($scope, car) {
        car.start();
});

angular.module('cars', [])
    .factory('car', function ($log, dieselEngine) {
        return {
            start: function() {
                $log.info('Starting ' + dieselEngine.type);
            };
        }
    })
    .factory('dieselEngine', function () {
        return {
            type: 'custom diesel'
        };
});


JQuery作为一个类库用于简化DOM操作,比如document遍历,事件处理,动画,Ajax交互等。
AngularJS是一个完整的框架,它是以model为中心的编程模式,而Jquery则是以DOM为中心的。
 

转载于:https://my.oschina.net/u/924064/blog/901391

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值