什么是范围原型/原型继承的AngularJS的细微差别? [英] What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

查看:342
本文介绍了什么是范围原型/原型继承的AngularJS的细微差别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

借助 API参考范围页面说:

一个范围的可以的从父范围继承。

A scope can inherit from a parent scope.

借助开发者指南范围页面说:

一个范围(中典型)从其父范围继承属性。

A scope (prototypically) inherits properties from its parent scope.

因此​​,没有一个孩子总是范围从中典型其父继承的范围?有没有例外?当它继承,是它总是正常的JavaScript原型继承?

So, does a child scope always prototypically inherit from its parent scope? Are there exceptions? When it does inherit, is it always normal JavaScript prototypal inheritance?

推荐答案

快速解答:结果
孩子通常范围从中典型其父继承的范围,但并非总是如此。一个例外是范围指令:{...} - 这将创建一个孤立的范围不中典型继承。创造一个可重用组件指令时,该结构经常被使用。

Quick answer:
A child scope normally prototypically inherits from its parent scope, but not always. One exception to this rule is a directive with scope: { ... } -- this creates an "isolate" scope that does not prototypically inherit. This construct is often used when creating a "reusable component" directive.

至于细微差别,范围继承是正常straightfoward ......直到你需要的 2路数据绑定(即表单元素,NG-模型)在子范围。伍重复,NG-开关,和NG-包括如果您尝试在父范围绑定到的原始(例如,数字,字符串,布尔)从孩子的范围内可你绊倒。它不工作的方式大多数人期望它应该工作。孩子得到的范围自有物业,隐藏/阴影的同名parent属性。你的变通办法

As for the nuances, scope inheritance is normally straightfoward... until you need 2-way data binding (i.e., form elements, ng-model) in the child scope. Ng-repeat, ng-switch, and ng-include can trip you up if you try to bind to a primitive (e.g., number, string, boolean) in the parent scope from inside the child scope. It doesn't work the way most people expect it should work. The child scope gets its own property that hides/shadows the parent property of the same name. Your workarounds are


  1. 定义的父对象的模型,然后引用该对象的属性在孩子:parentObj.someProp

  2. 使用$ parent.parentScopeProperty(并不总是可能的,但更容易比1.如果可能)

  3. 上定义的父范围的功能,并且从子调用它(并不总是可能的)



L-O-N-G答案

同时放置在AngularJS维基: https://github.com/angular/angular.js/wiki/Understanding-Scopes

先有原型继承的一个坚实的理解,特别是如果你是从服务器端背景的,你更failiar具有同级车中的iCal的继承是很重要的。所以,让我们回顾一下,第一。

It is important to first have a solid understanding of prototypal inheritance, especially if you are coming from a server-side background and you are more failiar with class-ical inheritance. So let's review that first.

假设parentScope具有属性ASTRING,aNumber的,anArray,anObject和机能缺失。如果childScope中典型的parentScope继承,我们有:

Suppose parentScope has properties aString, aNumber, anArray, anObject, and aFunction. If childScope prototypically inherits from parentScope, we have:

(请注意,为了节省空间,我展示了 anArray 对象作为其三个值一个蓝色的对象,而不是三个独立的灰色文字的单一蓝色对象。 )

(Note that to save space, I show the anArray object as a single blue object with its three values, rather than an single blue object with three separate gray literals.)

如果我们尝试访问从子范围parentScope定义的属性,JavaScript的将首先在孩子的范围,而不是找物业,然后看在继承范围,找物业。 (如果没有发现在parentScope物业,将继续检查原型链......一路到根范围内)。所以,这些都是正确的:

If we try to access a property defined on the parentScope from the child scope, JavaScript will first look in the child scope, not find the property, then look in the inherited scope, and find the property. (If it didn't find the property in the parentScope, it would continue up the prototype chain... all the way up to the root scope). So, these are all true:

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

假设我们再做到这一点:

Suppose we then do this:

childScope.aString = 'child string'

原型链不征求意见,和一个新的ASTRING属性添加到childScope。 这个新属性隐藏/阴影parentScope属性具有相同的名称。当我们讨论NG-重复及以下NG-包括这将变得非常重要。

The prototype chain is not consulted, and a new aString property is added to the childScope. This new property hides/shadows the parentScope property with the same name. This will become very important when we discuss ng-repeat and ng-include below.

假设我们再做到这一点:

Suppose we then do this:

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

原型链被咨询,因为对象(anArray和anObject)未在childScope找到。该对象在parentScope发现,并且属性值被更新的原始对象。没有新的属性被添加到childScope;没有创建新的对象。 (注意,在JavaScript数组和功能也都对象。)

The prototype chain is consulted because the objects (anArray and anObject) are not found in the childScope. The objects are found in the parentScope, and the property values are updated on the original objects. No new properties are added to the childScope; no new objects are created. (Note that in JavaScript arrays and functions are also objects.)

假设我们再做到这一点:

Suppose we then do this:

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

原型链不征求意见,以及儿童得到范围隐藏了两个新的对象属性/同名影着parentScope对象的属性。

The prototype chain is not consulted, and child scope gets two new object properties that hide/shadow the parentScope object properties with the same names.

小贴士:


  • 如果我们读childScope.propertyX和childScope有propertyX,那么原型链不查询。

  • 如果我们设置childScope.propertyX,原型链不查询。

最后一个场景:

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

我们先删除childScope属性,那么当我们再次尝试访问属性,原型链查询。

We deleted the childScope property first, then when we try to access the property again, the prototype chain is consulted.

竞争者:


  • 下创建新的范围,以及中典型继承:NG重复,NG-包括NG-开关,NG-控制器,指令与范围:真正的,指令与 transclude:真正的

  • 下面创建一个新的作用域不中典型继承:指令与范围:{...} 。这将创建一个孤立,而不是范围。

  • The following create new scopes, and inherit prototypically: ng-repeat, ng-include, ng-switch, ng-controller, directive with scope: true, directive with transclude: true.
  • The following creates a new scope which does not inherit prototypically: directive with scope: { ... }. This creates an "isolate" scope instead.

请注意,在默认情况下,指令不会创造新的范围 - 即默认为范围:假

Note, by default, directives do not create new scope -- i.e., the default is scope: false.

假设我们有我们的控制器:

Suppose we have in our controller:

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

而在我们的HTML:

And in our HTML:

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

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

每个NG-包括生成一个新的子范围,其中中典型从父继承范围

Each ng-include generates a new child scope, which prototypically inherits from the parent scope.

打字(比如77),到第一个输入文本框会导致孩子的范围来获得隐藏一个新的 myPrimitive scope属性/阴影的父作用域属性一样的名字。这可能不是你想要什么/期望的。

Typing (say, "77") into the first input textbox causes the child scope to get a new myPrimitive scope property that hides/shadows the parent scope property of the same name. This is probably not what you want/expect.

打字(比方说,99)放入第二输入文本框不导致一个新的子属性。由于tpl2.html模型绑定到一个对象的属性,原型继承踢的时候,ngModel查找对象myObject的 - 它发现它在父范围

Typing (say, "99") into the second input textbox does not result in a new child property. Because tpl2.html binds the model to an object property, prototypal inheritance kicks in when the ngModel looks for object myObject -- it finds it in the parent scope.

我们可以重写第一个模板使用$父,如果我们不希望我们的模型从原始更改为对象:

We can rewrite the first template to use $parent, if we don't want to change our model from a primitive to an object:

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

打字(比方说,22)放入该输入文本框不导致一个新的子属性。该模型现在绑定到父作用域的属性(因为$父是引用父作用域子作用域属性)。

Typing (say, "22") into this input textbox does not result in a new child property. The model is now bound to a property of the parent scope (because $parent is a child scope property that references the parent scope).

有关的所有范围(原型与否),角总是跟踪父子关系(即层次),通过作用域属性$父,$$ childHead和$$ childTail。我通常不显示在图中这些作用域属性。

For all scopes (prototypal or not), Angular always tracks a parent-child relationship (i.e., a hierarchy), via scope properties $parent, $$childHead and $$childTail. I normally don't show these scope properties in the diagrams.

有关,其中的形式的元素并不参与情况下,另一种解决方案是定义在父范围的函数来修改原语。然后,确保孩子总是调用这个函数,这将提供给孩子的范围,由于原型继承。如,

For scenarios where form elements are not involved, another solution is to define a function on the parent scope to modify the primitive. Then ensure the child always calls this function, which will be available to the child scope due to prototypal inheritance. E.g.,

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

下面是一个使用这种父函数的方式一样品小提琴。 (小提琴被写了这个答案的一部分:<一href=\"http://stackoverflow.com/a/14104318/215945\">http://stackoverflow.com/a/14104318/215945.)

Here is a sample fiddle that uses this "parent function" approach. (The fiddle was written as part of this answer: http://stackoverflow.com/a/14104318/215945.)

另请参阅 http://stackoverflow.com/a/13782671/215945 和<一个href=\"https://github.com/angular/angular.js/issues/1267\">https://github.com/angular/angular.js/issues/1267.

NG-开关范围继承工作就像NG-包括。所以,如果你需要在父范围,使用$父2路数据绑定到一个原始的,或改变模型是一个对象,然后绑定到该对象的属性。这将避免儿童藏匿范围父作用域属性/阴影。

ng-switch scope inheritance works just like ng-include. So if you need 2-way data binding to a primitive in the parent scope, use $parent, or change the model to be an object and then bind to a property of that object. This will avoid child scope hiding/shadowing of parent scope properties.

又见<一个href=\"http://stackoverflow.com/questions/12405005/angularjs-bind-scope-of-a-switch-case/12414410\">AngularJS,一个开关的情况下绑定范围?

伍重复的工作方式略有不同。假设我们有我们的控制器:

Ng-repeat works a little differently. Suppose we have in our controller:

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

而在我们的HTML:

And in our HTML:

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

对于每个项目/迭代,NG-重复创建一个新的范围,其中中典型从父范围继承,但也该项目的值赋给一个新属性的新子范围即可。 (新属性的名称是循环变量的名字)下面介绍一下源角code为NG-重复居然是:

For each item/iteration, ng-repeat creates a new scope, which prototypically inherits from the parent scope, but it also assigns the item's value to a new property on the new child scope. (The name of the new property is the loop variable's name.) Here's what the Angular source code for ng-repeat actually is:

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

如果项目是一个原始(如在myArrayOfPrimitives),基本上值的副本被分配给新的子范围属性。更改子作用域属性的值(即使用NG-模式,因此子作用域 NUM )做的不可以更改阵列父作用域引用。因此,在上面的第一个NG重复,每个孩子得到的范围在 NUM 属性,它是独立于myArrayOfPrimitives数组:

If item is a primitive (as in myArrayOfPrimitives), essentially a copy of the value is assigned to the new child scope property. Changing the child scope property's value (i.e., using ng-model, hence child scope num) does not change the array the parent scope references. So in the first ng-repeat above, each child scope gets a num property that is independent of the myArrayOfPrimitives array:

这NG重复是不行的(像你想/希望它)。键入到文本框改变了灰色框的值,这是只有在子作用域可见。我们要的是用于输入影响myArrayOfPrimitives数组,而不是一个子范围基本属性。要做到这一点,我们需要改变模型为对象的数组

This ng-repeat will not work (like you want/expect it to). Typing into the textboxes changes the values in the gray boxes, which are only visible in the child scopes. What we want is for the inputs to affect the myArrayOfPrimitives array, not a child scope primitive property. To accomplish this, we need to change the model to be an array of objects.

因此​​,如果项目是一个对象,对原始对象(不是复制)的引用被分配给新的子范围属性。更改子作用域属性的值(即使用NG-模式,因此 obj.num 确实改变对象的父范围引用。因此,在上面的第二个NG重复,我们有:

So, if item is an object, a reference to the original object (not a copy) is assigned to the new child scope property. Changing the child scope property's value (i.e., using ng-model, hence obj.num) does change the object the parent scope references. So in the second ng-repeat above, we have:

(Ⅰ着色一行灰色只是使得其清楚,其中它是将。)

(I colored one line gray just so that it is clear where it is going.)

这正常工作。键入到文本框改变灰色框,这是双方的孩子和家长作用域可见的值。

This works as expected. Typing into the textboxes changes the values in the gray boxes, which are visible to both the child and parent scopes.

又见<一个href=\"http://stackoverflow.com/questions/13714884/difficulty-with-ng-model-ng-repeat-and-inputs\">Difficulty与NG模型,NG-重复,和投入,并
http://stackoverflow.com/a/13782671/215945

采用NG-控制器结果在正常原型继承,就像嵌套控制器NG-包括和NG-开关,这样相同的技术应用。
不过,它被认为是不好的形式为两个控制器通过$范围继承来共享信息 - <一个href=\"http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/\">http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/
服务应该用于共享控制器之间的数据来代替。

Nesting controllers using ng-controller results in normal prototypal inheritance, just like ng-include and ng-switch, so the same techniques apply. However, "it is considered bad form for two controllers to share information via $scope inheritance" -- http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ A service should be used to share data between controllers instead.

(如果你真的想通过控制器范围继承来共享数据,有什么你需要做的。孩子范围将可以访问所有父作用域属性。
参见<一个href=\"http://stackoverflow.com/questions/13825419/controller-load-order-differs-when-loading-or-navigating/13843771#13843771\">Controller加载或导航)时加载顺序不同。

(If you really want to share data via controllers scope inheritance, there is nothing you need to do. The child scope will have access to all of the parent scope properties. See also Controller load order differs when loading or navigating)


  1. 默认值(范围:假) - 该指令不创建一个新的范围,所以这里没有继承。这是很容易的,但也危险的,因为,例如,指令可能会认为它正在对范围的一个新的属性,而实际上它是重挫现有属性。这不是那些旨在为可重用的组件编写指令一个不错的选择。

  2. 范围:真正的 - 指令创建一个新的子作用域中典型从父继承的范围。如果一个以上的指令(同一DOM元素)请求一个新的范围,只创建一个新的子范围。既然我们有正常的原型继承,这就像NG-包括和NG-开关,所以要谨慎2路数据绑定到父作用域原语,和儿童躲在范围/父作用域属性的阴影。

  3. 范围:{...} - 指令创建一个新的分离/隔离范围。它不中典型继承。这种创建可重用组件时,因为该指令不会意外读取或修改父范围通常是您最好的选择。然而,这种指令常常需要访问几个父范围的属性。对象的哈希用于建立双向绑定(用=)或单向的结合(使用@)父范围和分离物范围之间。也有'和;'绑定到父作用域的前pressions。所以,这些都创建一个从父范围派生局部范围的属性。
    请注意,使用属性来帮助您设置绑定 - 你不能只是引用对象的哈希父作用域属性名称,你必须使用一个属性。例如,如果你想绑定到孤立的范围parent属性 parentProp 这是不行的:&LT; D​​IV我-指令&GT; 范围:{localProp:@parentProp'} 。一个属性必须用于指定指令要结合每个parent属性:&LT; D​​IV我的指示性的单亲匡= parentProp&GT; 范围:{localProp:@theParentProp'}
    结果隔离示波器的 __ __原引用对象。
    隔离示波器的$父引用父范围,所以虽然它是独立的,不从父范围中典型继承,它仍然是一个孩子的范围。
    结果对于下面的图片中,我们有
    结果&LT;我的指导性插值={{parentProp1}}twowayBinding =parentProp2&GT;
    结果范围:{interpolatedProp:@interpolated',twowayBindingProp:'= twowayBinding'}
    结果另外,假设该指令做到这一点在其链接功能: scope.someIsolateProp =我很孤立
    结果
    结果欲了解更多信息,分离范围看<一个href=\"http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/\">http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/

  4. transclude:真正的 - 指令创建一个新的transcluded子范围,其中中典型从父继承的范围。该transcluded和孤立的范围(如果有的话)是兄弟 - 各范围的$ parent属性引用相同的父作用域。当transcluded和分离范围都存在,隔离scope属性$$ nextSibling将引用transcluded范围。我不知道与transcluded范围的任何细微差别。
    结果对于下面的图片,承担同样的指令与上面这种加法: transclude:真正的
    结果

  1. default (scope: false) - the directive does not create a new scope, so there is no inheritance here. This is easy, but also dangerous because, e.g., a directive might think it is creating a new property on the scope, when in fact it is clobbering an existing property. This is not a good choice for writing directives that are intended as reusable components.
  2. scope: true - the directive creates a new child scope that prototypically inherits from the parent scope. If more than one directive (on the same DOM element) requests a new scope, only one new child scope is created. Since we have "normal" prototypal inheritance, this is like ng-include and ng-switch, so be wary of 2-way data binding to parent scope primitives, and child scope hiding/shadowing of parent scope properties.
  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 directives 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"

    For more information on isolate scopes see http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
  4. transclude: true - the directive creates a new "transcluded" child scope, which prototypically inherits from the parent scope. The transcluded and the isolated scope (if any) are siblings -- the $parent property of each scope references the same parent scope. When a transcluded and an isolate scope both exist, isolate scope property $$nextSibling will reference the transcluded scope. I'm not aware of any nuances with the transcluded scope.
    For the picture below, assume the same directive as above with this addition: transclude: true

小提琴有一个 showScope()功能,可以用于检查一个隔离和transcluded范围。见在小提琴的意见的说明。

This fiddle has a showScope() function that can be used to examine an isolate and transcluded scope. See the instructions in the comments in the fiddle.

有四种类型的作用域:


  1. 正常范围原型继承 - NG-包括NG-开关,NG-控制器,指令与范围:真正的

  2. 用复制/赋值正常范围原型继承 - 纳克重复。 NG-每次循环创建新的子范围,而新的子范围始终获得一个新的属性。

  3. 隔离范围 - 以范围指令:{...} 。这个人是不是原型,而是'=','@'和'和;'提供了一种机制来访问父作用域属性,通过属性。

  4. transcluded范围 - 以指令transclude:真正的。这一次也是正常的原型范围继承,但它也是任何一个同级隔离范围。

  1. normal prototypal scope inheritance -- ng-include, ng-switch, ng-controller, directive with scope: true
  2. normal prototypal scope inheritance with a copy/assignment -- ng-repeat. Each iteration of ng-repeat creates a new child scope, and that new child scope always gets a new property.
  3. isolate scope -- directive with scope: {...}. This one is not prototypal, but '=', '@', and '&' provide a mechanism to access parent scope properties, via attributes.
  4. transcluded scope -- directive with transclude: true. This one is also normal prototypal scope inheritance, but it is also a sibling of any isolate scope.

有关的所有范围(原型与否),角总是跟踪父子关系(即层次),通过房产$家长和$$ childHead和$$ childTail。

For all scopes (prototypal or not), Angular always tracks a parent-child relationship (i.e., a hierarchy), via properties $parent and $$childHead and $$childTail.

图用 graphviz的*。点文件,这些文件在 github上。蒂姆·卡斯韦尔的学习JavaScript和对象图表是使用的GraphViz为图中的灵感。

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

这篇关于什么是范围原型/原型继承的AngularJS的细微差别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆