AngularJs Scope详解及示例代码


Posted in Javascript onSeptember 01, 2016

一、什么是Scope?

scope(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope)是一个指向应用model的object。它也是expression(http://www.cnblogs.com/lcllao/archive/2012/09/16/2687162.html)的执行上下文。scope被放置于一个类似应用的DOM结构的层次结构中。scope可以监测(watch,$watch)expression和传播事件。

二、scope的特性

  1. scope提供$watch API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$watch),用于监测model的变化。
  2. scope提供$apply API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$apply),在“Angular realm”(controller、server、angular event handler)之外,从系统到视图传播任何model的变化。
  3. scope可以在提供到被共享的model属性的访问的时候,被嵌入到独立的应用组件中。scope通过(原型),从parent scope中继承属性。
  4. scope在expression求值之时提供上下文环境。例如,{{username}}表达式是无意义的,除非它与一个特定的定义了”username”属性的scope一起进行求值。 

三、Scope as Data-Model(scope作为数据模型)

scope是在应用controller与view之间的纽带。在模版linking(http://www.cnblogs.com/lcllao/archive/2012/09/04/2669802.html)的阶段,directive(http://www.cnblogs.com/lcllao/archive/2012/09/09/2677190.html)在scope中设置$watch表达式。$watch让directive能够得知属性的变化,使得directive将更新后的值渲染到DOM中。

controller和directive两者都与scope有引用,但它们两者之间没有(引用)(Both controllers and directives have reference to the scope, but not to each other)。这样的安排,将controller从directive和DOM中隔离开来。这是一个重要的地方,因为它让controller与view是隔离的,极大地提升了应用的可测试性(greatly improves the testing story of the applications)。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app>
<head>
 <meta charset="UTF-8">
 <title>data-model</title>
 <style type="text/css">
  .ng-cloak {
   display: none;
  }
 </style>
</head>
<body class="ng-cloak">
<div ng-controller="MyController">
 你的名字: <input type="text" ng-model="username"/>
 <button ng-click="sayHello()">欢迎</button>
 <hr/>
 {{greeting}}
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 function MyController($scope) {
  $scope.username = "My Little Dada";
  $scope.sayHello = function() {
   $scope.greeting = "Hello~" + $scope.username + "!";
  };
 }
</script>
</body>
</html>

在上面的例子中我们可以注意到MyController以”My Little Dada”对scope中的username属性进行赋值。然后,scope通知input进行赋值,将username的值预先填入input中。这展示了controller如何做才能够写入数据到scope中。

相似地,controller可以将行为附加在scope中,正如那个当用户点击“欢迎”按钮时触发的sayHello方法一样。sayHello方法可以读取username属性,也可以创建greeting属性。这表明,当它们绑定到HTML input控件时,scope中的属性会自动更新。

逻辑上,显示{{greeting}}涉及以下两点:

与定义了{{greeting}}表达式的模版DOM节点一起检索scope。在这个例子中,这个scope与传递到MyController中的scope是相同的。(我们在稍后将会讨论scope的层次结构)
通过之前检索的scope,对greeting表达式进行求值,然后将结果作为封闭DOM元素的text的值。

我们可以认为,scope和它自己的属性可以作为数据,用于渲染视图。scope是所有和view相关的东西单一的真相来源(The scope is the single source-of-truth for all things view related)。

从可测试性来看,controller和view的分离是值得欣喜的,因为它允许我们在没有渲染细节的干扰下(专注于)测试行为。

it('should say hello', function() {
 var scopeMock = {};
 var cntl = new MyController(scopeMock);
 
 // Assert that username is pre-filled
 expect(scopeMock.username).toEqual('World');
 
 // Assert that we read new username and greet
 scopeMock.username = 'angular';
 scopeMock.sayHello();
 expect(scopeMock.greeting).toEqual('Hello angular!');
});

四、Scope Hierarchies(scope层次结构)

每一个angular应用有且只有一个root scope,但可以拥有多个child scope。

应用可以拥有多个child scope,因为一些directive会创建新的child scope(参考directive文档,查看哪些directive可创建新的scope,如ng-repeat)。当新的scope被创建后,他们将作为一个child scope,加入到parent scope中。这样,创建了一个与它们附属的DOM相似的树结构。

当angular对{{username}}求值时,它首先查看与当前元素关联的scope的username属性。如果没有找到对应的属性,它将会一直向上搜索parent scope,直到到达root scope。在javascript中,这个行为被称为“原型继承”,child scope典型地继承自它们的parent。

这个例子说明应用中的scope(是怎样的),属性的原型继承。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app>
<head>
 <meta charset="UTF-8">
 <title>scope-hierarchies</title>
 <style type="text/css">
  .ng-cloak {
   display: none;
  }
  .ng-scope {
   border: 1px dashed red;
  }
 </style>
</head>
<body class="ng-cloak">
<div ng-controller="MyController">
 经理:{{employee.name}} [{{department}}] <br/>
 报告:
 <ul>
  <li ng-repeat="employee in employee.reports">
   {{employee.name}} [{{department}}]
  </li>
 </ul>
 <hr/>
 {{greeting}}
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 function MyController($scope) {
  $scope.department = "某部";
  $scope.employee = {
   name:"My Little Dada",
   reports: [
    {name:"Lcllao"},
    {name:"那个谁^o^"}
   ]
  };
 }
</script>
</body>
</html>

注意,angular自动放置ng-scope class到与scope粘附的元素中。<style>定义在上面的例子中,通过红色的虚线,高亮新的scope的范围。因为repeater对{{employee.name}}表达式求值,child scope是必须的,但取决于表达式在哪一个scope进行求值,不同的scope有不同的结果。相似地,{{department}}的值是从root scope中原型继承得来的,只有在那个地方有,才有department属性的定义。

五、Retrieving Scopes from the DOM(从DOM中检索scope)

scope作为$scope数据属性附加到DOM中,可以被用于以调试作为目的的检索。(在应用中通过这个方式检索Scope是不可能的。)附加到的DOM的root scope的位置是通过ng-app directive的位置定义的。通常ng-app是放置在<html>元素中,但它也可以放置在其他元素中,例如,只有一部分视图需要被angular控制。

在debugger中查看scope:

1. 在浏览器中,对着感兴趣的元素点击右键,选择“查看元素”。我们可以看到浏览器debugger高亮了我们选中的元素。

2. debugger允许我们在console中通过$0变量去访问当前选择的元素。

3. 想查看关联的scope,我们可以在console中输入:angular.element($0).scope()

六、Scope Events Propagation(Scope事件传播)

scope可以以类似于DOM事件的方式进行事件传播。事件可以被broadcast(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$broadcast)到child scope或者emit(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$emit)到parent scope中。(当前scope如果有监听,也会执行)

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app>
<head>
 <meta charset="UTF-8">
 <title>scope-event-propagation</title>
 <style type="text/css">
  .ng-cloak {
   display: none;
  }
 </style>
</head>
<body class="ng-cloak">
<div ng-controller="MyController">
 root scope count:{{count}}
 <ul>
  <li ng-repeat="i in [1]" ng-controller="MyController">
   <button ng-click="$emit('MyEvent')">$emit("MyEvent")</button>
   <button ng-click="$broadcast('MyEvent')">$broadcast("MyEvent")</button>
   <br/>
   middle scope count:{{count}}
   <ul>
    <li ng-repeat="item in [1,2]" ng-controller="MyController">
     Leaf scope count:{{count}}
    </li>
   </ul>
  </li>
 </ul>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
 function MyController($scope) {
  $scope.count = 0;
  $scope.$on("MyEvent", function() {
   $scope.count++;
  });
 }
</script>
</body>
</html>

七、Scope Life Cycle(scope生命周期)

浏览器正常的事件流中,当浏览器接收到事件后,它会执行一个相应的javascript回调。一旦回调函数执行完毕后,浏览器将会重绘DOM,并返回到继续等待事件的状态。

当浏览器在angular执行环境外调用javascript代码时,这意味着angular是不知道model的改变的。要正确处理model的修改,这个命令必须通过使$apply方法进入angular执行环境。只有在$apply方法中的model变更,才会正确地被angular统计。例如,一个directive监听了DOM事件,例如ng-click,它必须在$apply方法中对表达式进行求值。

在对表达式求值之后,$apply方法执行一个$digest。在$digest阶段里,scope检查所有$watch监听的表达式,将现在的值与旧的值作比较。脏检查(dirty checking)是异步的。这意味着赋值语句(例如$scope.username=”angular”)将不会马上导致一个$watch被通知,反而,$watch的通知将会延迟到$digest阶段。这个延迟是必须的,因为它把多个model更新联合到一个$watch通知中,这保证了在$watch通知的过程中,没有其他$watch在执行。如果一个$watch改变了model的值,那么它将会强制增加一个$digest周期。

1) Creation(创建scope)

root scope是在应用启动的过程中,被$injector(http://code.angularjs.org/1.0.2/docs/api/AUTO.$injector)创建的。在模版linking的过程中,一些directive会创建新的child scope。

2) Watcher registration(注册watcher)

在模版linking过程中,directive在scope中注册$watch。这些watch将会被用作向DOM传播model的值。

3) Model mutation(Model变化)

为了让变化被正确地检测,我们需要将他们包裹在scope.$apply中。(angular API 已经隐式地做了这部操作,所以,当在controller中做同步的工作或者与$http或者$timeout一起做异步工作的时候,不需要额外的$apply调用)。

4) Mutation observation(变化监测)

在$apply的结尾,angular会在root scope执行一个$digest周期,这将会传播到所有child scope中。在$digest周期中,所有注册了$watch的表达式或者function都会被检查,判断model是否发生了改变,如果改变发生了,那么对应的$watch监听器将会被调用。

5) Scope destruction(scope销毁)

当child scope不再是必须的时候,child scope的产生者有责任通过scope.$destroy() API销毁它们(child scope)。这将会停止$digest的调用传播传播到child scope中,让被child scope model使用的内存可以被gc(garbage collector)回收。

1. Scopes and Directives

在编译阶段中,compiler依靠DOM模版匹配directive。directive通常可以分为两大类:

观察型directive(Observing directives),例如dobule-curly表达式{{expression}},使用$watch方法注册监听器。无论什么时候,表达式(的值)发生改变,这类directive必须被通知,从而更新view。
监听型directive(Listener directive),例如ng-click,注册一个监听器到DOM中。当DOM的监听器触发时,directive会执行相关的表达式,并通过使用$apply方法更新视图。

当一个外部的事件(例如用户动作、timer或者XHR)被监听到,相关的expression必须通过$apply方法应用到scope中,让所有监听器能够正确地更新。

2. Directives that Create Scopes

在大多数的情况中,directive和scope是相互影响的,但不会创建新的scope实例。然而,一些directive(例如ng-controller和ng-repeat)会创建新scope,附加child scope到对应的DOM元素中。我们通过使用angular.element(aDomElement).scope()查看任意DOM元素的scope。

3. Controllers and Scopes

在以下的情况中,scope与controller是相互影响的:

  1. controller使用scope暴露controller方法到模版中(查看ng-controller(http://code.angularjs.org/1.0.2/docs/api/ng.directive:ngController))。
  2. controller定义方法(行为),可以改变model(scope上的属性)。
  3. controller可能在model中注册watch。这些watch会在controller行为执行之后马上执行。

4. Scope $watch Performance Considerations(Scope $watch的性能考虑)

在angular中,为了检测属性的变化而对scope进行脏检测(Dirty checking),是一个普遍的操作。为此,这要求dirty checking函数必须是高效的。应小心dirty checking函数不要做任何DOM访问操作,因为DOM访问的速度比访问javascript对象属性的速度要慢好几个数量级。

 以上就是关于AngularJS Scope 的资料整理,后续继续补充相关资料,谢谢大家对本站的支持!

Javascript 相关文章推荐
Gird组件 Part-3:范例RSSFeed Viewer
Mar 10 Javascript
自己整理的一个javascript日期处理函数
Oct 16 Javascript
30个让人兴奋的视差滚动(Parallax Scrolling)效果网站
Mar 04 Javascript
js实现绿白相间竖向网页百叶窗动画切换效果
Mar 02 Javascript
jQuery实现监控页面所有ajax请求的方法
Dec 10 Javascript
浅谈JS原型对象和原型链
Mar 02 Javascript
canvas快速绘制圆形、三角形、矩形、多边形方法介绍
Dec 29 Javascript
对存在JavaScript隐式类型转换的四种情况的总结(必看篇)
Aug 31 Javascript
VueJs使用Amaze ui调整列表和内容页面
Nov 30 Javascript
JavaScript设计模式之职责链模式应用示例
Aug 07 Javascript
Node.js 使用request模块下载文件的实例
Sep 05 Javascript
element-ui table span-method(行合并)的实现代码
Dec 20 Javascript
AngularJs Modules详解及示例代码
Sep 01 #Javascript
AngularJs IE Compatibility 兼容老版本IE
Sep 01 #Javascript
AngularJs 国际化(I18n/L10n)详解
Sep 01 #Javascript
AngularJs Forms详解及简单示例
Sep 01 #Javascript
vue.js入门教程之计算属性
Sep 01 #Javascript
AngularJs expression详解及简单示例
Sep 01 #Javascript
分享JS代码实现鼠标放在输入框上输入框和图片同时更换样式
Sep 01 #Javascript
You might like
php 获取一个月第一天与最后一天的代码
2010/05/16 PHP
PHP CURL CURLOPT参数说明(curl_setopt)
2013/09/30 PHP
解决cPanel无法安装php5.2.17
2014/06/22 PHP
php设计模式之抽象工厂模式分析【星际争霸游戏案例】
2020/01/23 PHP
php使用pthreads v3多线程实现抓取新浪新闻信息操作示例
2020/02/21 PHP
来自chinaz的ajax获取评论代码
2008/05/03 Javascript
JS 进度条效果实现代码整理
2011/05/21 Javascript
基于jQuery实现的当离开页面时出现提示的实现代码
2011/06/27 Javascript
stream.js 一个很小、完全独立的Javascript类库
2011/10/28 Javascript
用js获取电脑信息(是使用与IE浏览器)
2013/01/15 Javascript
jquery如何判断某元素是否具备指定的样式
2013/11/05 Javascript
jQuery中text() val()和html()的区别实例详解
2016/06/28 Javascript
jQuery ajax调用后台aspx后台文件的两种常见方法(不是ashx)
2016/06/28 Javascript
基于jQuery实现滚动切换效果
2016/12/02 Javascript
JS仿京东移动端手指拨动切换轮播图效果
2020/04/10 Javascript
jQuery插件之validation插件
2017/03/29 jQuery
利用Vue.js+Node.js+MongoDB实现一个博客系统(附源码)
2017/04/24 Javascript
ES7中利用Await减少回调嵌套的方法详解
2017/11/01 Javascript
vuex实现登录状态的存储,未登录状态不允许浏览的方法
2018/03/09 Javascript
Vue+Mock.js模拟登录和表格的增删改查功能
2018/07/26 Javascript
详解js动态获取浏览器或页面等容器的宽高
2019/03/13 Javascript
微信小程序 可搜索的地址选择实现详解
2019/08/28 Javascript
ckeditor一键排版功能实现方法分析
2020/02/06 Javascript
Vue使用Element实现增删改查+打包的步骤
2020/11/25 Vue.js
利用Pandas 创建空的DataFrame方法
2018/04/08 Python
Python处理中文标点符号大集合
2018/05/14 Python
TensorFLow 变量命名空间实例
2020/02/11 Python
500行python代码实现飞机大战
2020/04/24 Python
SISLEY希思黎官方旗舰店:享誉全球的奢华植物美容品牌
2018/04/25 全球购物
外贸业务员求职信范文
2013/12/12 职场文书
乐观大学生的自我评价
2014/01/10 职场文书
纪念建党演讲稿范文
2014/01/13 职场文书
师恩难忘教学反思
2014/04/27 职场文书
2014离婚协议书范文两篇
2014/09/15 职场文书
员工试用期工作总结
2019/06/20 职场文书
500字作文之难忘的同学
2019/12/20 职场文书