API参考范围页面表示:
作用域可以继承自父作用域。
开发者指南范围页面表示:
作用域(原型)从其父作用域继承属性。
快速回答:子作用域通常从其父作用域原型继承,但并不总是如此。此规则的一个例外是带有scope: { ... }的指令-这会创建一个不原型继承的“隔离”作用域。此构造通常在创建“可重用组件”指令时使用。
scope: { ... }
至于细微差别,作用域继承通常很简单……直到你在子作用域中需要双向数据绑定(即表单元素、ng-model)。如果你尝试从子作用域内绑定到父作用域中的原始(例如,数字、字符串、布尔值),则NG重复、ng开关和ng包含会让你出错。它的工作方式并不像大多数人期望的那样。子作用域有自己的属性,隐藏/阴影同名的父属性。你的解决方法是
新的AngularJS开发人员通常没有意识到ng-repeat、ng-switch、ng-view、ng-include和ng-if都创建了新的子作用域,因此当涉及这些指令时,问题往往会出现。
ng-repeat
ng-switch
ng-view
ng-include
ng-if
通过遵循在你的NG-模型中总是有个'.'的“最佳实践”-观看3分钟的价值,可以轻松避免原语的这个问题。Misko演示了ng-switch的原语绑定问题。
在您的模型中使用“.”将确保原型继承发挥作用。所以,使用
<input type="text" ng-model="someObj.prop1"> <!--rather than<input type="text" ng-model="prop1">`-->
也放在AngularJS wiki上:https://github.com/angular/angular.js/wiki/Understanding-Scopes
首先对原型继承有一个扎实的理解是很重要的,特别是如果你来自服务器端背景并且你更熟悉类继承。所以让我们先回顾一下。
假设父母范围具有属性aString、aNumber、anArray、anObject和aFunction。如果子范围原型通常继承自父母范围,我们有:
(请注意,为了节省空间,我将anArray对象显示为具有三个值的单个蓝色对象,而不是具有三个单独灰色文字的单个蓝色对象。)
anArray
如果我们尝试从子作用域访问在父作用域上定义的属性,JavaScript将首先在子作用域中查找,而不是找到该属性,然后在继承的作用域中查找,并找到该属性。(如果它在父作用域中没有找到该属性,它将继续沿着原型链……一直到根作用域)。所以,这些都是真的:
childScope.aString === 'parent string'childScope.anArray[1] === 20childScope.anObject.property1 === 'parent prop1'childScope.aFunction() === 'parent output'
假设我们这样做:
childScope.aString = 'child string'
不参考原型链,并将一个新的aString属性添加到子范围中。这个新属性隐藏/阴影具有相同名称的父母范围属性。当我们在下面讨论ng-重复和ng-包含时,这将变得非常重要。
childScope.anArray[1] = '22'childScope.anObject.property1 = 'child prop1'
之所以要查阅原型链,是因为在子范围中找不到对象(anArray和anObject)。对象在父范围中找到,并且在原始对象上更新属性值。不会向子范围添加新属性;不会创建新对象。(请注意,在JavaScript中,数组和函数也是对象。)
childScope.anArray = [100, 555]childScope.anObject = { name: 'Mark', country: 'USA' }
不参考原型链,子作用域获得两个新的对象属性,这些属性隐藏/隐藏具有相同名称的父作用域对象属性。
外卖:
最后一个场景:
delete childScope.anArraychildScope.anArray[1] === 22 // true
我们首先删除了ChilScope属性,然后当我们再次尝试访问该属性时,会参考原型链。
竞争者:
scope: true
transclude: true
请注意,默认情况下,指令不创建新范围——即默认值为scope: false。
scope: false
假设我们在控制器中:
$scope.myPrimitive = 50;$scope.myObject = {aNumber: 11};
在我们的超文本标记语言中:
<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-包括都会生成一个新的子作用域,该子作用域通常从父作用域继承。
在第一个输入文本框中键入(例如,“77”)会导致子作用域获得一个新的myPrimitive作用域属性,该属性隐藏/阴影同名的父作用域属性。这可能不是您想要/期望的。
myPrimitive
在第二个输入文本框中键入(比如"99")不会导致新的子属性。因为tpl2.html将模型绑定到一个对象属性,所以当ngModel查找对象myObject时,原型继承就开始了它在父作用域中找到了它。
如果我们不想将模型从原语更改为对象,我们可以重写第一个模板以使用$父:
<input ng-model="$parent.myPrimitive">
在这个输入文本框中键入(例如,"22")不会导致新的子属性。模型现在绑定到父作用域的属性(因为$父是引用父作用域的子作用域属性)。
对于所有的作用域(无论原型与否),Angular总是通过作用域属性$父,$子和$子跟踪父子关系(即层次结构)。我通常不会在图表中显示这些作用域属性。
对于不涉及表单元素的场景,另一种解决方案是在父作用域上定义一个函数来修改原语。然后确保子始终调用此函数,由于原型继承,该函数将可用于子作用域。例如,
// in the parent scope$scope.setMyPrimitive = function(value) {$scope.myPrimitive = value;}
这是一个使用“父函数”方法的样品小提琴。(小提琴是作为答案的一部分编写的:https://stackoverflow.com/a/14104318/215945。)
请参阅https://stackoverflow.com/a/13782671/215945和https://github.com/angular/angular.js/issues/1267。
ng-Switch作用域继承的工作方式就像ng-包括一样。因此,如果你需要双向数据绑定到父作用域中的原语,请使用$父,或者将模型更改为对象,然后绑定到该对象的属性。这将避免父作用域属性的子作用域隐藏/阴影。
另见AngularJS,绑定开关案例的范围?
NG重复的工作方式略有不同。假设我们在控制器中:
$scope.myArrayOfPrimitives = [ 11, 22 ];$scope.myArrayOfObjects = [{num: 101}, {num: 202}]
<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-重复创建一个新作用域,该作用域原型继承自父作用域但它也将项目的值分配给新子作用域上的新属性。(新属性的名称是循环变量的名称。)这是ng-重复的Angular源代码的实际内容:
childScope = scope.$new(); // child scope prototypically inherits from parent scope...childScope[valueIdent] = value; // creates a new childScope property
如果Item是基元(如在myArrayOfPrimitics中),本质上是将值的副本分配给新的子作用域属性。更改子作用域属性的值(即使用ng-model,因此子作用域num)确实会没有更改父作用域引用的数组。因此,在上面的第一个ng-重复中,每个子作用域都获得一个独立于myArrayOfPrimi的num属性数组:
num
这个ng重复不会起作用(就像你想要/期望的那样)。在文本框中键入会更改灰色框中的值,这些值仅在子作用域中可见。我们想要的是输入影响myArrayOfPrime数组,而不是子作用域原始属性。要实现这一点,我们需要将模型更改为对象数组。
因此,如果Item是一个对象,则将对原始对象(而不是副本)的引用分配给新的子作用域属性。更改子作用域属性的值(即使用ng-model,因此obj.num)确实更改父作用域引用的对象。所以在上面的第二个ng-重复中,我们有:
obj.num
(我将一行涂成灰色,以便清楚它要去哪里。
这按预期工作。在文本框中键入会更改灰色框中的值,这些值对子范围和父范围都可见。
请参阅ng-模型、ng-重复和输入的困难和https://stackoverflow.com/a/13782671/215945
使用ng-控制器嵌套控制器会导致正常的原型继承,就像ng-包括和ng-Switch一样,因此适用相同的技术。但是,“两个控制器通过$scope继承共享信息被认为是不好的形式”--http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/应该使用服务在控制器之间共享数据。
(如果您真的想通过控制器范围继承共享数据,则无需做任何事情。子范围将有权访问所有父范围属性。请参阅加载或导航时控制器加载顺序不同)
parentProp
<div my-directive>
scope: { localProp: '@parentProp' }
<div my-directive the-Parent-Prop=parentProp>
scope: { localProp: '@theParentProp' }
__proto__
<my-directive interpolated="\{\{parentProp1}}" twowayBinding="parentProp2">
scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
scope.someIsolateProp = "I'm isolated"
此小提琴有一个showScope()函数,可用于检查隔离和隔离作用域。请参阅小提琴注释中的说明。
showScope()
有四种类型的范围:
scope: {...}
对于所有作用域(无论是否为原型),Angular总是通过属性$父级和$子级和$子级跟踪父子关系(即层次结构)。
图表是用graviz“*. dot”文件生成的,这些文件位于github上。Tim Caswell的“使用对象图学习JavaScript”是使用GraphViz制作图表的灵感。
我绝不想与马克的答案竞争,但只是想突出显示最终使所有内容都成为JavaScript继承及其原型链的新人的作品。
只有属性读取搜索原型链,而不是写入。所以当你设置
myObject.prop = '123';
它不会向上看链子,但是当你设置
myObject.myThing.prop = '123';
在那个写操作中有一个微妙的读操作尝试在写入其prop之前查找myThing。这就是为什么从子对象写入object.properties会到达父对象的原因。
我想在@Scott Driscoll的回答中添加一个javascript原型继承的例子。我们将使用Object.create()的经典继承模式,这是EcmaScript 5规范的一部分。
首先我们创建“父”对象函数
function Parent(){ }
然后将原型添加到“父”对象函数
Parent.prototype = {primitive : 1,object : {one : 1}}
创建“子”对象函数
function Child(){ }
分配子原型(使子原型继承父原型)
Child.prototype = Object.create(Parent.prototype);
分配适当的“子”原型构造函数
Child.prototype.constructor = Child;
向子原型添加方法“change eProps”,这将重写子对象中的“原始”属性值,并更改子对象和父对象中的“object.one”值
Child.prototype.changeProps = function(){this.primitive = 2;this.object.one = 2;};
启动父(父)和子(子)对象。
var dad = new Parent();var son = new Child();
Call Children(son)change eProps方法
son.changeProps();
检查结果。
父基元属性没有改变
console.log(dad.primitive); /* 1 */
子基元属性已更改(重写)
console.log(son.primitive); /* 2 */
父子object.one属性已更改
console.log(dad.object.one); /* 2 */console.log(son.object.one); /* 2 */
工作示例http://jsbin.com/xexurukiso/1/edit/
更多关于Object.create的信息https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create