Angular.Js之Scope作用域的学习教程


Posted in Javascript onApril 27, 2017

scope是什么?

大家都知道在AngularJS 中作用域是一个指向应用模型的对象,它是表达式的执行环境。作用域有层次结构,这个层次和相应的 DOM 几乎是一样的。作用域能监控表达式和传递事件。

在 HTML 代码中,一旦一个 ng-app 指令被定义,那么一个作用域就产生了,由 ng-app 所生成的作用域比较特殊,它是一个根作用域($rootScope),它是其他所有$Scope 的最顶层。

除了用 ng-app 指令可以产生一个作用域之外,其他的指令如 ng-controller,ng-repeat 等都会产生一个或者多个作用域。此外,还可以通过 AngularJS 提供的创建作用域的工厂方法来创建一个作用域。这些作用域都拥有自己的继承上下文,并且根作用域都为$rootScope。

在生成一个作用域之后,在编写 AngularJS 代码时,$scope 对象就代表了这个作用域的数据实体,我们可以在$scope 内定义各种数据类型,之后可以直接在 HTML 中以 {{变量名}} 方式来让 HTML 访问到这个变量。

继承作用域

AngularJS 在创建一个作用域时,会检索上下文,如果上下文中已经存在一个作用域,那么这个新创建的作用域就会以 JavaScript 原型继承机制继承其父作用域的属性和方法。

一些 AngularJS 指令会创建新的子作用域,并且进行原型继承: ng-repeat、ng-include、ng-switch、ng-view、ng-controller, 用 scope: true transclude: true 创建的 directive。

以下 HTML 中定义了三个作用域,分别是由 ng-app 指令所创建的$rootScope,parentCtrl 和 childCtrl 所创建的子作用域,这其中 childCtrl 生成的作用域又是 parentCtrl 的子作用域。

示例一:作用域的继承实例

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
 angular.module('app', [])
  .controller('parentCtrl', ['$scope', function($scope) {
  $scope.args= 'Nick DeveloperWorks';
  }])
  .controller('childCtrl', ['$scope', function($scope) {
  $scope.args= 'Nick DeveloperWorks for test';
  }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
 <input ng-model="args">
 <div ng-controller="childCtrl">
 <input ng-model="args">
 </div>
</div>
</body>
</html>

继承作用域符合 JavaScript 的原型继承机制,这意味着如果我们在子作用域中访问一个父作用域中定义的属性,JavaScript 首先在子作用域中寻找该属性,没找到再从原型链上的父作用域中寻找,如果还没找到会再往上一级原型链的父作用域寻找。在 AngularJS 中,作用域原型链的顶端是$rootScope,AnguarJS 将会寻找到$rootScope 为止,如果还是找不到,则会返回 undefined。

我们用实例代码说明下这个机制。首先,我们探讨下对于原型数据类型的作用域继承机制:

示例二:作用域继承实例-原始类型数据继承

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
 angular.module('app', [])
  .controller('parentCtrl', ['$scope', function($scope) {
  $scope.args = 'Nick DeveloperWorks';
  }])
  .controller('childCtrl', ['$scope', function($scope) {
  }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
 <input ng-model="args">
 <div ng-controller="childCtrl">
 <input ng-model="args">
 </div>
</div>
</body>
</html>

测试运行结果:

第一个输入框:

虽然在 childCtrl 中没有定义具体的 args 属性,但是因为 childCtrl 的作用域继承自 parentCtrl 的作用域,因此,AngularJS 会找到父作用域中的 args 属性并设置到输入框中。而且,如果我们在第一个输入框中改变内容,内容将会同步的反应到第二个输入框。

第二个输入框:

第二个输入框的内容从此将不再和第一个输入框的内容保持同步。在改变第二个输入框的内容时,因为 HTML 代码中 model 明确绑定在 childCtrl 的作用域中,因此 AngularJS 会为 childCtrl 生成一个 args 原始类型属性。这样,根据 AngularJS 作用域继承原型机制,childCtrl 在自己的作用域找得到 args 这个属性,从而也不再会去寻找 parentCtrl 的 args 属性。从此,两个输入框的内容所绑定的属性已经是两份不同的实例,因此不会再保持同步。

现将代码做如下修改,结合以上两个场景,会出现怎样的结果?

示例三:作用域继承实例-对象数据继承

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
 angular.module('app', [])
  .controller('parentCtrl', ['$scope', function($scope) {
  $scope.args = {};
  $scope.args.content = 'Nick DeveloperWorks';
  }])
  .controller('childCtrl', ['$scope', function($scope) {
  }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
 <input ng-model="args.content">
 <div ng-controller="childCtrl">
 <input ng-model="args.content">
 </div>
</div>
</body>
</html>

测试结果是无论改变任何一个输入框的内容,两者的内容始终同步。

根据 AngularJS 的原型继承机制,如果 ng-model 绑定的是一个对象数据,那么 AngularJS 将不会为 childCtrl 创建一个 args 的对象,自然也不会有 args.content 属性。这样,childCtrl 作用域中将始终不会存在 args.content 属性,只能从父作用域中寻找,也即是两个输入框的的变化其实只是在改变 parentCtrl 作用域中的 args.content 属性。因此,两者的内容始终保持同步。

我们再看一个例子,分析结果如何。

示例四:作用域继承实例-不再访问父作用域的数据对象。

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
 angular.module('app', [])
  .controller('parentCtrl', ['$scope', function($scope) {
  $scope.args = {};
  $scope.args.content = 'Nick DeveloperWorks';
  }])
  .controller('childCtrl', ['$scope', function($scope) {
  $scope.args = {};
  $scope.args.content = 'Nick DeveloperWorks for test';
  }]);
</script>
<body ng-app="app">
<div ng-controller="parentCtrl">
 <input ng-model="args.content">
 <div ng-controller="childCtrl">
 <input ng-model="args.content">
 </div>
</div>
</body>
</html>

测试结果是两个输入框的内容永远不会同步。子作用域有实例数据对象,则不访问父作用域。

独立作用域

独立作用域是 AngularJS 中一个非常特殊的作用域,它只在 directive 中出现。在对 directive 的定义中,我们添加上一个 scope:{} 属性,就为这个 directive 创建出了一个隔离作用域。

示例5: directive 创建出一个孤立作用域

angular.module('isolate', []).directive("isolate", function () {
 return {
 scope : {},
 };
})

独立作用域最大的特点是不会原型继承其父作用域,对外界的父作用域保持相对的独立。因此,如果在定义了孤立作用域的 AngularJS directive 中想要访问其父作用域的属性,则得到的值为 undefined。代码如下:

示例六:独立作用域的隔离性

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script type="text/javascript">
 angular.module('app', [])
  .controller('ctrl', ['$scope', function($scope) {
  $scope.args = {};
  }])
  .directive("isolateDirective", function () {
  return {
   scope : {},
   link : function($scope, $element, $attr) {
   console.log($scope.$args); //输出 undefined
   }
  };
  });
</script>
<body ng-app="app">
<div ng-controller="ctrl">
 <div isolate-directive></div>
</div>
</body>
</html>

上面的代码中通过在 directive 中声明了 scope 属性从而创建了一个作用域,其父作用域为 ctrl 所属的作用域。但是,这个作用域是孤立的,因此,它访问不到父作用域的中的任何属性。存在这样设计机制的好处是:能够创建出一些列可复用的 directive,这些 directive 不会相互在拥有的属性值上产生串扰,也不会产生任何副作用。

AngularJS 独立作用域的数据绑定

在继承作用域中,我们可以选择子作用域直接操作父作用域数据来实现父子作用域的通信,而在独立作用域中,子作用域不能直接访问和修改父作用域的属性和值。为了能够使孤立作用域也能和外界通信,AngularJS 提供了三种方式用来打破独立作用域“孤立”这一限制。

单向绑定(@ 或者 @attr)

这是 AngularJS 独立作用域与外界父作用域进行数据通信中最简单的一种,绑定的对象只能是父作用域中的字符串值,并且为单向只读引用,无法对父作用域中的字符串值进行修改,此外,这个字符串还必须在父作用域的 HTML 节点中以 attr(属性)的方式声明。

使用这种绑定方式时,需要在 directive 的 scope 属性中明确指定引用父作用域中的 HTML 字符串属性,否则会抛异常。示例代码如下:

实例七: 单向绑定示例

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
 angular.module('isolateScope', [])
  .directive("isolateDirective", function () {
  return {
   replace : true,
   template: '<button>{{isolates}}</button>',
   scope : {
   isolates : '@',
   },
   link : function($scope, $element, $attr) {
   $scope.isolates = "DeveloperWorks";//无效
   }
  };
  })
  .controller("ctrl", function ($scope) {
  $scope.btns = 'NICK';
  });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
 <button>{{btns}}</button>
 <div isolate-directive isolates="{{btns}}"></div>
</div>
</body>
</html>

上面的代码,通过在 directive 中声明了 scope:{isolates:'@'} 使得 directive 拥有了父作用域中 data-isolates (isolates为自定义属性,不加data也可以,但建议加上data)这个 HTML 属性所拥有的值,这个值在控制器 ctrl 中被赋值为'nick'。所以,代码的运行结果是页面上有两个名为 nick的按钮。

我们还注意到 link 函数中对 isolates 进行了修改,但是最终不会在运行结果中体现。这是因为 isolates 始终绑定为父作用域中的 btns 字符串,如果父作用域中的 btns 不改变,那么在孤立作用域中无论怎么修改 isolates 都不会起作用。

通过这种形式的绑定,孤立作用域将有能力访问到父作用域中的函数对象,从而能够执行父作用域中的函数来获取某些结果。这种方式的绑定跟单向绑定一样,只能以只读的方式访问父作用函数,并且这个函数的定义必须写在父作用域 HTML 中的 attr(属性)节点上。

这种方式的绑定虽然无法修改父作用域的 attr 所设定的函数对象,但是却可以通过执行函数来改变父作用域中某些属性的值,来达到一些预期的效果。示例代码如下:

示例八:引用绑定示例

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
 angular.module('isolateScope', [])
   .directive("isolateDirective", function () {
    return {
     replace : true,
     scope : {
      isolates : '&',
     },
     link : function($scope, $element, $attr) {
      var func = $scope.isolates();
      func();
     }
    };
   })
   .controller("ctrl", function ($scope) {
    $scope.func = function () {
     console.log("Nick DeveloperWorks");
    }
   });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
 <div isolate-directive data-isolates="func"></div>
</div>
</body>
</html>

这个例子中,浏览器的控制台将会打印“Nick DeveloperWorks”文字。

上面的代码中我们在父作用域中指定了一个函数对象$scope.func,在孤立作用域中通过对 HTML 属性的绑定从而引用了 func。需要注意的是 link 函数中对 func 对象的使用方法,$scope.isolates 获得的仅仅是函数对象,而不是调用这个对象,因此我们需要在调用完$scope.isolates 之后再调用这个函数,才能得到真正的执行结果。

双向绑定(=或者=attr)

双向绑定赋予 AngularJS 孤立作用域与外界最为自由的双向数据通信功能。在双向绑定模式下,孤立作用域能够直接读写父作用域中的属性和数据。和以上两种孤立作用域定义数据绑定一样,双向绑定也必须在父作用域的 HTML 中设定属性节点来绑定。

双向绑定非常适用于一些子 directive 需要频繁和父作用域进行数据交互,并且数据比较复杂的场景。不过,由于可以自由的读写父作用域中的属性和对象,所以在一些多个 directive 共享父作用域数据的场景下需要小心使用,很容易引起数据上的混乱。

示例代码如下:

示例九:双向绑定示例

<!doctype html>
<html>
<head>
 <meta charset=utf-8"/>
 <title>scope nick</title>
 <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>
</head>
<script>
 angular.module('isolateScope', [])
   .directive("isolateDirective", function () {
    return {
     replace : true,
     template: '<button>{{isolates}}</button>',
     scope : {
      isolates : '=',
     },
     link : function($scope, $element, $attr) {
      $scope.isolates.name = "NICK";
     }
    };
   })
   .controller("ctrl", function ($scope) {
    $scope.btns = {
     name : 'nick',
     dw : 'DeveloperWorks'
    };
   });
</script>
<body ng-app="isolateScope" >
<div ng-controller="ctrl">
 <button>{{btns.dw}}</button>
 <button>{{btns.name}}</button>
 <div isolate-directive data-isolates="btns"></div>
</div>
</body>
</html>

上面的代码运行的结果是浏览器页面上出现三个按钮,其中第一个按钮标题为“DeveloperWorks”,第二和第三个按钮的标题为“NICK”。

初始时父作用域中的$scope.btns.name为小写的“nick”,通过双向绑定,孤立作用域中将父作用域的 name改写成为大写的“NICK”并且直接生效,父作用域的值被更改。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JavaScript 动态改变图片大小
Jun 11 Javascript
DOM 基本方法
Jul 18 Javascript
jquery CSS选择器笔记
Mar 29 Javascript
javascript判断用户浏览器插件安装情况的代码
Jan 01 Javascript
详解JavaScript中Date.UTC()方法的使用
Jun 12 Javascript
js+css实现select的美化效果
Mar 24 Javascript
BootStrap文件上传样式超好看【持续更新】
May 10 Javascript
基于jQuery倒计时插件实现团购秒杀效果
May 13 Javascript
react中使用css的7中方式(最全总结)
Feb 11 Javascript
小程序关于请求同步的总结
May 05 Javascript
JavaScript遍历数组和对象的元素简单操作示例
Jul 09 Javascript
vue + el-form 实现的多层循环表单验证
Nov 25 Vue.js
JS简单实现点击按钮或文字显示遮罩层的方法
Apr 27 #Javascript
JavaScript通过改变文字透明度实现的文字闪烁效果实例
Apr 27 #Javascript
jQuery实现动态添加、删除按钮及input输入框的方法
Apr 27 #jQuery
Three.js的使用及绘制基础3D图形详解
Apr 27 #Javascript
jquery ui sortable拖拽后保存位置
Apr 27 #jQuery
ES5 ES6中Array对象去除重复项的方法总结
Apr 27 #Javascript
JavaScript实现隐藏省略文字效果的方法
Apr 27 #Javascript
You might like
PHP Mysql编程之高级技巧
2008/08/27 PHP
php 用checkbox一次性删除多条记录的方法
2010/02/23 PHP
php将会员数据导入到ucenter的代码
2010/07/18 PHP
JScript中的&quot;this&quot;关键字使用方式补充材料
2007/03/08 Javascript
js如何获取file控件的完整路径具体实现代码
2013/05/15 Javascript
Javascript的setTimeout()使用闭包特性时需要注意的问题
2014/09/23 Javascript
使用JavaScript和CSS实现文本隔行换色的方法
2015/11/04 Javascript
基于Javascript实现弹出页面效果
2016/01/01 Javascript
在Vue中使用echarts的实例代码(3种图)
2017/07/10 Javascript
JS+HTML+CSS实现轮播效果
2017/11/28 Javascript
在vue项目中正确使用iconfont的方法
2018/09/28 Javascript
js canvas实现星空连线背景特效
2019/11/01 Javascript
openlayers 3实现车辆轨迹回放
2020/09/24 Javascript
JavaScript实现alert弹框效果
2020/11/19 Javascript
python数据处理实战(必看篇)
2017/06/11 Python
浅谈pytorch和Numpy的区别以及相互转换方法
2018/07/26 Python
详解python中Numpy的属性与创建矩阵
2018/09/10 Python
python 两个一样的字符串用==结果为false问题的解决
2020/03/12 Python
Django实现whoosh搜索引擎使用jieba分词
2020/04/08 Python
如何在scrapy中集成selenium爬取网页的方法
2020/11/18 Python
Pycharm在指定目录下生成文件和删除文件的实现
2020/12/28 Python
浅谈HTML5 defer和async的区别
2016/06/07 HTML / CSS
超30万乐谱下载:Musicnotes.com
2016/09/24 全球购物
YSL圣罗兰美妆美国官网:Yves Saint Lauret US
2016/11/21 全球购物
Monnier Frères美国官网:法国知名奢侈品网站
2016/11/22 全球购物
德国网上药房:Apotal
2017/04/04 全球购物
什么是"引用"?申明和使用"引用"要注意哪些问题?
2016/03/03 面试题
平面设计的岗位职责
2013/11/08 职场文书
实习自我评价怎么写
2013/12/02 职场文书
《都江堰》教学反思
2014/02/07 职场文书
弘扬民族精神演讲稿
2014/05/07 职场文书
2014年办公室主任工作总结
2014/11/12 职场文书
违反学校规则制度检讨书
2015/01/01 职场文书
门卫岗位职责
2015/02/09 职场文书
2015年乡镇统计工作总结
2015/04/22 职场文书
导游词之苏州盘门景区
2019/11/12 职场文书