AngularJS 中的 $scope 是什么?附带示例的教程

⚡ 智能摘要

在 AngularJS 中,`$Scope` 是一个内置对象,用于将控制器与其视图绑定。本页解释了作用域的创建、行为函数、父子层级结构、与 `$rootScope` 的区别,以及摘要循环如何刷新绑定。

  • 🔗 绑定作用: $scope 将成员变量从控制器传递到视图,因此模板无需直接操作 DOM 即可渲染数据。
  • ⚙️ 行为功能: 当视图需要对点击、输入更改或其他用户事件做出反应时,将函数附加到 $scope。
  • 🌳 范围层级: ng-controller 和 ng-repeat 等指令会创建子作用域,这些子作用域会反映应用程序的 DOM 树。
  • 🌍 $rootScope 范围: 每个应用程序都有一个 $rootScope,它的行为就像一个全局对象,每个子作用域都可以读取它。
  • 🔍 消化循环: $watch 注册监听器,$apply 进入 AngularJS 执行上下文,$digest 比较值直到模型稳定。
  • 🚀 表现习惯: 保持监视表达式的开销较小,避免在其中访问 DOM,并对不再需要的作用域调用 $destroy。

AngularJS 中的 $Scope 是什么?

AngularJS 中的 $scope 是什么?

AngularJS 中的 $scope 是一个内置对象,它基本上绑定了“控制器”和“视图”。可以在控制器的范围内定义成员变量,然后视图可以访问这些变量。

每个控制器 AngularJS 应用程序通过依赖注入获得自己的 $scope 实例。任何附加到该对象的内容都会对控制器所管理的模板可见,因此 $scope 充当了位于应用程序和模板之间的共享数据模型。 Java脚本逻辑和你的 HTML 标记。

请考虑以下示例:

AngularJS 中的 $scope

angular.module('app',[]).controller('HelloWorldCtrl',
	function($scope)
	{
		$scope.message = "Hello World"
	});

Code 说明:

  1. 模块名称为“app”
  2. 控制器的名称是“HelloWorldCtrl”。
  3. 范围对象是用于将信息从控制器传递到视图的主要对象。
  4. 成员变量已添加到范围对象

⚠️ 语法警告:.controller() 该方法接受两个参数:控制器名称和工厂函数,因此两者之间需要用逗号分隔。 'HelloWorldCtrl'function($scope)省略逗号会导致错误 Uncaught SyntaxError 在 AngularJS 启动之前。

单个控制器和单个视图只是问题的一半。实际应用中,控制器会嵌套,列表会重复使用,控件也会重用,这意味着同时存在多个作用域。

AngularJS 中的作用域层次结构和继承是如何运作的

每个 AngularJS 应用程序都只有一个根作用域,但可以有任意数量的子作用域。指令创建这些子作用域并将它们附加到它们所修饰的 DOM 元素上,因此作用域树反映了渲染页面的结构。

当 AngularJS 计算如下表达式时: {{siteName}}它首先在元素所属的作用域中查找该属性。如果该属性不存在,则查找范围会扩展到父作用域,并不断向上追溯,直到到达根作用域。 Java脚本开发者将这种行为称为原型继承,子作用域正是以这种方式继承自父作用域。

下面的示例将一个控制器嵌套在另一个控制器中,以便您可以观察继承过程。

<div ng-app="scopeDemo">
    <div ng-controller="ParentCtrl">
        Parent scope value: {{siteName}}
        <div ng-controller="ChildCtrl">
            Child reads parent value: {{siteName}}
            Child own value: {{lesson}}
        </div>
    </div>
</div>

<script type="text/javascript">
    var app = angular.module("scopeDemo", []);

    app.controller("ParentCtrl", function($scope) {
        $scope.siteName = "Guru99";
    });

    app.controller("ChildCtrl", function($scope) {
        $scope.lesson = "AngularJS Scope";
    });
</script>

Code 说明:

  1. ParentCtrl 定义 siteName 就其自身范围而言。
  2. ChildCtrl 从未定义 siteName然而,内部的div仍然会打印出来。 Guru99 因为查找操作会向上追溯到父级作用域。
  3. lesson 它只存在于子作用域中,因此父 div 无法读取它。

哪些指令会创建新的作用域?

  • ng-controller: 为它所在的元素创建一个子作用域。
  • ng-repeat: 每次迭代创建一个子作用域,这就是为什么同一表达式的每一行可以包含不同的值。
  • 自定义指令: 可以要求使用儿童瞄准镜,或者 隔离 故意不继承其父级的作用域。
  • 组件指令: 任何已在以下机构注册的 .component() 辅助函数始终接收一个隔离作用域。

由于子作用域是向上读取的,开发者常常会想知道这条链的顶端在哪里。这个顶层对象叫做 $rootScope。

$scope 与 $rootScope:有什么区别?

AngularJS $rootScope `$rootScope` 是整个应用程序的作用域。一个应用程序只能有一个 `$rootScope`,它像全局变量一样使用。每个 `$scope` 都是一个子作用域,而 `$rootScope` 是位于作用域树顶端的父作用域。下表比较了这两个对象。

方面 范围 $rootScope
每个应用程序的实例数 多个,每个控制器或作用域创建指令一个 正好一个
创建者 ng-controller 和 ng-repeat 等指令 应用程序引导期间的注入器
提升品牌曝光性 它自身的元素及其所有后代 应用程序中的每个作用域
典型用途 一个视图或一个列表行的数据和行为 应用程序范围的值,例如已登录用户
主要风险 分支外部的值变得无法访问 名称冲突和隐藏耦合,例如全局变量

谨慎使用 `$rootScope`。任何放置在那里的内容都会在整个应用程序中存在,并且可以被任何地方读取,因此共享服务通常是存放全局状态的更安全选择。官方 $rootScope 引用 列出完整的 API。

了解数据存储位置只是工作的一半。另一半是教会范围如何响应用户的操作。

在 AngularJS 中设置或添加行为

为了对事件做出反应或在视图中执行某种计算/处理,我们必须为范围提供行为。

行为被添加到范围对象以响应视图可能触发的特定事件。一旦在控制器中定义了行为,视图就可以访问它。

让我们看一个例子来了解如何实现这一点。

设置或添加行为

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta chrset="UTF 8">
    <title>Guru99</title>     
</head>  
<body ng-app="DemoApp">
<h1> Guru99 Global Event</h1>
<script src="https://code.angularjs.org/1.6.9/angular.js"></script>
<div ng-controller="DemoController">
	{{fullName("Guru","99")}}
</div>
<script type="text/javascript">  
	var app = angular.module("DemoApp", []);
	app.controller("DemoController", function($scope) {
    
    $scope.fullName=function(firstName,lastname){
		return firstName + lastname;
		}
	} );
</script> 

</body>  
</html>

Code 说明:

  1. 我们正在创建一个名为“fullName”的行为。此行为是一个接受 2 个参数(firstName、lastname)的函数。
  2. 然后,行为返回这两个参数的连接。
  3. 在视图中,我们调用了该行为并传入了两个值“Guru“ ”和“99”作为参数传递给该行为。

如果命令成功执行,则在浏览器中运行代码时将显示以下输出。

输出:

设置或添加行为

在浏览器中,您将看到这两个值的连接结果。 Guru 99 个被传递给了行为 调节器.

像上面这样的绑定会自动刷新,在应用程序规模扩大之前,了解实现这一功能的机制是值得的。

为什么范围观察者和摘要周期如此重要

AngularJS 通过脏检查而非变更事件来保持视图与作用域的同步。三个作用域 API 驱动着这一过程:

  • $watch: 为表达式注册监听器。例如,插值 {{fullName}} 在模板链接期间为您注册关注者。
  • $apply: 它会进入 AngularJS 执行上下文,执行你的代码,然后触发摘要。它仅在自定义事件回调或第三方库回调中需要,因为内置指令已经调用了它。
  • 摘要: 遍历 $rootScope 及其子级上的每个监视器,将当前值与前一个值进行比较,并重复此过程,直到模型不再改变。

由于摘要信息可能需要多次遍历,因此监视表达式必须保持低开销。应避免在监视表达式内部访问 DOM,因为读取 DOM 的速度比读取摘要信息慢几个数量级。 Java脚本属性。

监测深度也会影响成本。通过参考值监测速度最快,但只有当整个值被替换时才会注意到。 $watchCollection 监听数组或对象中新增、删除或重新排序的元素。按值监听会在每次摘要时遍历嵌套结构,是开销最大的选项,因此应最后使用。

常见问题

隔离作用域不会从其父级继承原型。使用 `.component()` 辅助方法构建的组件指令始终会获得一个隔离作用域,这使得可重用的小部件独立于周围的控制器,并防止意外读取父级属性。

`$emit` 会向上遍历父作用域,直至 `$rootScope`,而 `$broadcast` 会向下遍历所有子作用域,直至其所有子作用域。两者都通过 `$on` 接收。由于 `$broadcast` 会遍历所有子作用域,因此应谨慎使用。

当子作用域不再需要时,调用 `scope.$destroy()`。这将停止摘要信息向该作用域的传播,并允许垃圾回收器回收其模型。同时,通过调用 `$watch` 返回的函数来注销手动监听器。

助理,例如 GitHub 副驾驶 虽然能快速生成作用域样板代码,但仍然会出现一些错误,例如控制器注册中缺少逗号。请对照官方文档检查每个生成的代码片段。 AngularJS 作用域文档.

AI 助手将 $scope 属性映射到组件类字段,并将 $watch 逻辑重写为信号或 RxJS 流。将它们与官方的 角度方向 需要编写文档并运行单元测试,因为生成的转换经常会遗漏继承的作用域查找。

总结一下这篇文章: