The API Reference Scope page says:

作用域can继承自父作用域.

Developer Guide Scope page人说:

作用域(原型)从其父作用域继承属性.

  • 那么,子作用域总是从父作用域继承原型吗?
  • 有例外吗?
  • 当它确实继承时,它总是正常的JavaScript原型继承吗?

推荐答案

Quick answer:

至于细微差别,范围继承通常是直截了当的...直到子范围中需要2-way data binding(即表单元素、ng模型).如果试图从子作用域内部绑定到父作用域中的primitive(例如,数字、字符串、布尔值),则Ng repeat、Ng switch和Ng include可能会使您出错.它不像大多数人期望的那样工作.子作用域获取自己的属性,该属性隐藏/隐藏同名的父属性.你的变通办法是

  1. 在父对象中为模型定义对象,然后在子对象中引用该对象的属性:parentObj.一些props
  2. 使用$parent.parentScopeProperty(并非总是可行,但在可能的情况下比1容易)
  3. 在父作用域上定义一个函数,然后从子作用域调用它(并不总是可能的)

新的AngularJS开发人员通常没有意识到ng重复ngswitch ng-viewng包括ng-if都会创建新的子作用域,因此当涉及这些指令时,问题往往会出现.(有关问题的快速说明,请参见this example.)

通过遵循always have a '.' in your ng-models-观看3分钟的"最佳实践",可以很容易地避免原语的这个问题.Misko演示了ngswitch 的原始绑定问题.

有一个"."在您的模型中,将确保原型继承发挥作用.所以,使用

<input type="text" ng-model="someObj.prop1">

<!--rather than
<input type="text" ng-model="prop1">`
-->


L-o-n-g answer:

JavaScript原型继承

Also placed on the AngularJS wiki: https://github.com/angular/angular.js/wiki/Understanding-Scopes

首先要扎实理解原型继承,特别是如果您来自服务器端背景并且更熟悉类继承,这一点很重要.那么让我们先来回顾一下这一点.

假设parentScope具有aString、aNumber、anArray、anObject和aFunction属性.如果childScope原型继承自parentScope,我们有:

原型遗传

(请注意,为了节省空间,我将anArray对象显示为带有三个值的单个蓝色对象,而不是带有三个单独灰色文字的单个蓝色对象.)

如果我们try 从子范围访问在parentScope上定义的属性,JavaScript将首先在子范围内查找,而不是找到该属性,然后在继承范围内查找并找到该属性.(如果它没有在parentScope中找到属性,它将继续沿着原型链向上.一直到根作用域).所以,这些都是真的:

childScope.aString === 'parent string'
childScope.anArray[1] === 20
childScope.anObject.property1 === 'parent prop1'
childScope.aFunction() === 'parent output'

假设我们这样做:

childScope.aString = 'child string'

不参考原型链,并向childScope添加了一个新的aString属性.This new property hides/shadows the parentScope property with the same name.当我们在下面讨论ng repeat和ng include时,这将变得非常重要.

属性隐藏

假设我们这样做:

childScope.anArray[1] = '22'
childScope.anObject.property1 = 'child prop1'

由于在childScope中找不到对象(anArray和anObject),因此会参考prototype链.这些对象位于parentScope中,属性值在原始对象上更新.未向childScope添加新属性;不会创建新对象.(请注意,在JavaScript中,数组和函数也是对象.)

跟随原型链

假设我们这样做:

childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }

不参考原型链,子作用域获得两个新的对象属性,它们隐藏/隐藏具有相同名称的父作用域对象属性.

更多属性隐藏

外卖:

  • 如果我们读childScope.propertyX,而childScope拥有propertyX,则不参考原型链.
  • 如果我们设置childScope.propertyX,未咨询原型链.

最后一个场景:

delete childScope.anArray
childScope.anArray[1] === 22  // true

我们首先删除了childScope属性,然后当我们再次try 访问该属性时,会参考原型链.

删除子属性后


Angular 范围继承

竞争者:

  • 以下代码创建了新的作用域,并继承了原型:ng重复、ng包括、ngswitch 、ng-Controller、带scope: true的指令、带transclude: true的指令.
  • 下面创建了一个不继承原型的新作用域:带有scope: { ... }的指令.这会创建一个"隔离"范围.

注意,缺省情况下,指令不会创建新的作用域--即缺省值为scope: false.

ng包括

假设我们的控制器中有:

$scope.myPrimitive = 50;
$scope.myObject    = {aNumber: 11};

在我们的HTML中:

<script type="text/ng-template" id="/tpl1.html">
<input ng-model="myPrimitive">
</script>
<div ng包括 src="'/tpl1.html'"></div>

<script type="text/ng-template" id="/tpl2.html">
<input ng-model="myObject.aNumber">
</script>
<div ng包括 src="'/tpl2.html'"></div>

每个ng include生成一个新的子作用域,该子作用域通常从父作用域继承.

ng包括子作用域

在第一个输入文本框中键入(比方说,"77")会导致子范围获得一个新的myPrimitive范围属性,该属性隐藏/隐藏同名的父范围属性.这可能不是您想要/期望的.

ng包含基本体

在第二个输入文本框中键入(例如,"99")不会产生新的子属性.因为tpl2.html将模型绑定到一个对象属性,当ngModel查找对象myObject时,原型继承开始生效——它在父作用域中找到对象myObject.

ng-包含在对象中

如果我们不想将模型从基本体更改为对象,可以重写第一个模板,使用$parent:

<input ng-model="$parent.myPrimitive">

在此输入文本框中键入(比方说,"22")不会产生新的子属性.模型现在绑定到父作用域的属性(因为$Parent是引用父作用域的子作用域属性).

ng包含$parent

对于所有作用域(无论是否为原型),ANGLE始终通过作用域属性$Parent、$$Child Head和$$ChildTail跟踪父子关系(即层次 struct ).我通常不会在图表中显示这些作用域属性.

对于不涉及表单元素的场景,另一种解决方案是在父范围上定义一个函数来修改原语.然后确保子级始终调用此函数,由于原型继承,该子范围可以使用该函数.例如,

// in the parent scope
$scope.setMyPrimitive = function(value) {
     $scope.myPrimitive = value;
}

下面是一个使用这种"父函数"方法的sample fiddle.(这把小提琴是作为这个答案的一部分写的:https://stackoverflow.com/a/14104318/215945.)

另请参见https://stackoverflow.com/a/13782671/215945https://github.com/angular/angular.js/issues/1267.

ngswitch

ng switch作用域继承的工作原理与ng include一样.因此,如果需要将双向数据绑定到父范围中的原语,请使用$parent,或者将模型更改为对象,然后绑定到该对象的属性.这将避免子范围隐藏/隐藏父范围属性.

另见第AngularJS, bind scope of a switch-case?

ng重复

Ng-repeat works a little differently. 假设我们的控制器中有:

$scope.myArrayOfPrimitives = [ 11, 22 ];
$scope.myArrayOfObjects    = [{num: 101}, {num: 202}]

在我们的HTML中:

<ul><li ng重复="num in myArrayOfPrimitives">
       <input ng-model="num">
    </li>
<ul>
<ul><li ng重复="obj in myArrayOfObjects">
       <input ng-model="obj.num">
    </li>
<ul>

对于每个项目/迭代,ng repeat都会创建一个新的范围,该范围通常从父范围but it also assigns the item's value to a new property on the new child scope继承.(新属性的名称是循环变量的名称.)以下是ng repeat的Angular源代码:

childScope = scope.$new();  // child scope prototypically inherits from parent scope
...
childScope[valueIdent] = value;  // creates a new childScope property

如果项是基元(如myArrayOfPrimitives中的基元),则该值的一个副本本质上被分配给新的子范围属性.更改子作用域属性的值(即,使用ng模型,因此子作用域为num)会更改父作用域引用的array.因此,在上面的第一次ng重复中,每个子作用域都会获得一个独立于myArrayOfPrimitives数组的num属性:

ng使用原语重复

这种重复不起作用(就像你希望/期望的那样).在文本框中键入会更改灰色框中的值,这些值仅在子作用域中可见.我们想要的是输入影响myArrayOfPrimitives数组,而不是子范围原语属性.要实现这一点,我们需要将模型更改为对象array.

因此,如果项是对象,则对原始对象(而不是副本)的引用将指定给新的子范围属性.更改子范围属性的值(即,使用ng模型,因此为obj.num)does更改父范围引用的对象.在上面的第二次ng重复中,我们有:

ng对对象重复

(我把一条线涂成灰色,这样就可以清楚地知道它要go 哪里.)

这是意料之中的事.在文本框中键入会更改灰色框中的值,这些值对子作用域和父作用域都可见.

另见Difficulty with ng-model, ng重复, and inputs

ng控制器

使用ng-Controller的嵌套控制器会导致正常的原型继承,就像ng包括和ngswitch 一样,因此同样的技术也适用. 但是,"两个控制器通过$Scope继承共享信息被认为是不好的形式"--http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ 应该使用服务在控制器之间共享数据.

(如果您确实希望通过控制器作用域继承共享数据,则无需执行任何操作.子作用域将可以访问所有父作用域属性.

指令

  1. 默认值(scope: false)-该指令不创建新的作用域,因此这里没有继承.这很容易,但也很危险,因为例如,指令可能认为它正在范围上创建一个新属性,而实际上它正在 destruct 一个现有属性.对于编写作为可重用组件的指令来说,这不是一个好的 Select .
  2. scope: true-该指令创建一个新的子作用域,该子作用域通常继承自父作用域.如果多个指令(在同一个DOM元素上)请求新的作用域,则只创建一个新的子作用域.因为我们有"正常"的原型继承,这类似于ng包括和ngswitch ,所以要小心父作用域原语的双向数据绑定,以及父作用域属性的子作用域隐藏/隐藏.
  3. scope: { ... } - the directive creates a new isolate/isolated scope. It does not prototypically inherit. This is usually your best choice when creating reusable components, since the directive cannot accidentally read or modify the parent scope. However, such 指令 often need access to a few parent scope properties. The object hash is used to set up two-way binding (using '=') or one-way binding (using '@') between the parent scope and the isolate scope. There is also '&' to bind to parent scope expressions. So, these all create local scope properties that are derived from the parent scope. Note that attributes are used to help set up the binding -- you can't just reference parent scope property names in the object hash, you have to use an attribute. E.g., this won't work if you want to bind to parent property parentProp in the isolated scope: <div my-directive> and scope: { localProp: '@parentProp' }. An attribute must be used to specify each parent property that the directive wants to bind to: <div my-directive the-Parent-Prop=parentProp> and scope: { localProp: '@theParentProp' }.
    Isolate scope's __proto__ references Object. Isolate scope's $parent references the parent scope, so although it is isolated and doesn't inherit prototypically from the parent scope, it is still a child scope.
    For the picture below we have
    <my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2"> and
    scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
    Also, assume the directive does this in its linking function: scope.someIsolateProp = "I'm isolated"
    isolated scope
    For more information on isolate scopes see http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
  4. transclude: true-该指令创建一个新的"转置"子作用域,该子作用域通常从父作用域继承.被转移的作用域和隔离的作用域(如果有的话)是同级的——每个作用域的$parent属性都引用相同的父作用域.当转移范围和隔离范围都存在时,隔离范围属性$$nextSibling将引用转移范围.我不知道这个隐蔽的范围有什么细微差别.

fiddle具有可用于判断隔离和转移范围的showScope()功能.请参阅小提琴中注释中的说明.


总结

有四种类型的作用域:

  1. 正常的原型作用域继承——ng include、ng switch、ng controller、带scope: true的指令
  2. 带有复制/赋值的正常原型作用域继承--ng repeat.ng repeat的每次迭代都会创建一个新的子范围,并且该新的子范围始终会获得一个新属性.
  3. 隔离作用域--指令为scope: {...}.这不是原型,但是‘=’、‘@’和‘&;’提供了一种通过属性访问父作用域属性的机制.
  4. 转移范围——指令带transclude: true.这也是正常的原型作用域继承,但它也是任何孤立作用域的同级.

对于所有作用域(原型或非原型),Angular始终通过属性$parent和$$childHead和$$childTail跟踪父子关系(即层次 struct ).

Diagrams were generated with "*.dot" files, which are on github. Tim Caswell's "Learning JavaScript with Object Graphs" was the inspiration for using GraphViz for the diagrams.

Javascript相关问答推荐

为什么我的useDispatch挂钩在这里设置不正确?

d3可排序表标题行中有收件箱--如何使收件箱不触发排序?

获取加载失败:获取[.]添加时try 将文档添加到Firerestore,Nuxt 3

按钮未放置在html dis位置

如何将连续的十六进制字符串拆分为以空间分隔的十六进制块,每个十六进制块包含32个二元组?

通过嵌套模型对象进行Mongoose搜索

如何找出摆线表面上y与x相交的地方?

在react js中使用react—router—dom中的Link组件,分配的右侧不能被 destruct ''

如何将Map字符串,布尔值转换为{key:string;value:bo布尔值;}[]?<>

使用LocaleCompare()进行排序时,首先使用大写字母

我怎么才能得到Kotlin的密文?

如何在ASP.NET JavaScript中使用Google Charts API仅对绘制为负方向的条形图移动堆叠条形图标签位置

从Nextjs中的 Select 项收集值,但当单击以处理时,未发生任何情况

使用NextJS+MongoDB+Prisma ORM获取无效请求正文,无法发布错误

如何使用基于promise (非事件emits 器)的方法来传输数据?

为什么当我更新数据库时,我的所有组件都重新呈现?

当代码另有说明时,随机放置的圆圈有时会从画布上消失

我想使用GAS和HTML将从Electron 表格中获得的信息插入到文本字段的初始值中

在验证和提交表单后使用useNavigate()进行react 重定向,使用带有加载器和操作的路由

react 路由DOM有条件地呈现元素