AngularJS中范围原型/原型继承的细微差别是什么?
API Reference Scope page说:
Developer Guide Scope page说:
那么,子范围总是从原型继承自父范围吗?有例外吗?当它继承时,它是否总是正常的JavaScript原型继承?
快速回答:
子范围通常从其父范围继承,但不总是。此规则的一个例外是具有范围的指令:{…} – 这创建了一个“原型”继承的“isolation”范围。当创建“可重用组件”伪指令时,通常使用此构造。 至于细微差别,范围继承通常是直接的…直到你需要在子范围中的双向数据绑定(即表单元素,ng-model)。如果您尝试在子范围内从父范围绑定到一个图元(例如,数字,字符串,布尔值),Ng-repeat,ng-switch和ng-它不工作的方式大多数人期望它应该工作。子范围获取其自己的属性,隐藏/阴影同名的父属性。您的解决方法是 >在您的模型的父代中定义对象,然后在子代:parentObj.someProp中引用该对象的属性 L-o-n-g答案: JavaScript原型继承 也放在AngularJS wiki:https://github.com/angular/angular.js/wiki/Understanding-Scopes 重要的是首先对原型继承有一个坚实的理解,特别是如果你来自服务器端背景,并且你更熟悉类继承。所以让我们先看看。 假设parentScope具有属性aString,aNumber,anArray,anObject和aFunction。如果childScope从parentScope原型继承,我们有: (请注意,为了节省空间,我将anArray对象显示为具有三个值的单个蓝色对象,而不是具有三个单独灰色文本的单个蓝色对象。) 如果我们尝试从子范围访问parentScope上定义的属性,JavaScript将首先查找子范围,找不到属性,然后查找继承的范围,并找到属性。 (如果没有在parentScope中找到属性,它将继续向上的原型链…一直到根范围)。所以,这些都是真实的: childScope.aString === 'parent string' childScope.anArray[1] === 20 childScope.anObject.property1 === 'parent prop1' childScope.aFunction() === 'parent output' 假设我们这样做: childScope.aString = 'child string' 未咨询原型链,并且将新的aString属性添加到childScope。此新属性使用相同的名称隐藏/隐藏parentScope属性。当我们在下面讨论ng-repeat和ng-include时,这将变得非常重要。 假设我们这样做: childScope.anArray[1] = '22' childScope.anObject.property1 = 'child prop1' 参考原型链是因为在childScope中找不到对象(anArray和anObject)。对象在parentScope中找到,并且属性值在原始对象上更新。没有新的属性添加到childScope;不会创建新对象。 (请注意,在JavaScript数组和函数也是对象。) 假设我们这样做: childScope.anArray = [100,555] childScope.anObject = { name: 'Mark',country: 'USA' } 未咨询原型链,子作用域将获取两个新的对象属性,以使用相同的名称隐藏/隐藏parentScope对象属性。 外卖: >如果我们读childScope.propertyX,并且childScope有propertyX,那么不参考原型链。 最后一个场景: delete childScope.anArray childScope.anArray[1] === 22 // true 我们首先删除了childScope属性,然后当我们再次访问该属性时,会查询原型链。 角度范围继承 竞争者: >以下创建新作用域,并原型继承:ng-repeat,ng-include,ng-switch,ng-controller,指令,范围:true,directive和transclude:true。 注意,默认情况下,指令不创建新范围 – 即,默认值为scope:false。 ng-include 假设我们在我们的控制器: $scope.myPrimitive = 50; $scope.myObject = {aNumber: 11}; 在我们的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-include生成一个新的子范围,它从父范围原型继承。 在第一个输入文本框中键入(例如“77”)将导致子范围获得一个新的myPrimitive scope属性,该属性隐藏/隐藏同一名称的父作用域属性。这可能不是你想要/期望。 在第二个输入文本框中键入(例如“99”)不会产生新的子属性。因为tpl2.html将模型绑定到对象属性,当ngModel查找对象myObject时,原型继承会发生 – 它在父作用域中找到它。 我们可以重写第一个模板以使用$ parent,如果我们不想将我们的模型从一个基本类型更改为一个对象: <input ng-model="$parent.myPrimitive"> 在此输入文本框中键入(例如,“22”)不会产生新的子属性。该模型现在绑定到父作用域的属性(因为$ parent是引用父作用域的子作用域属性)。 对于所有范围(原型或非原型),Angular总是通过范围属性$ parent,$$ childHead和$$ childTail跟踪父子关系(即层次结构)。我通常不在图中显示这些范围属性。 对于不涉及表单元素的情况,另一个解决方案是在父作用域上定义一个函数来修改该基元。然后确保孩子总是调用这个函数,这将由于原型继承而可用于子范围。例如。, // in the parent scope $scope.setMyPrimitive = function(value) { $scope.myPrimitive = value; } 这里是一个使用这种“父函数”方法的sample fiddle。 (小提琴是作为这个答案的一部分写的:http://stackoverflow.com/a/14104318/215945.) 参见http://stackoverflow.com/a/13782671/215945和https://github.com/angular/angular.js/issues/1267。 ng开关 ng-switch范围继承的工作原理与ng-include类似。因此,如果您需要双向数据绑定到父作用域中的基元,请使用$ parent,或将模型更改为对象,然后绑定到该对象的属性。这将避免父作用域属性的子作用域隐藏/阴影。 参见AngularJS,bind scope of a switch-case? ng重复 Ng-repeat的工作方式略有不同。假设我们在我们的控制器: $scope.myArrayOfPrimitives = [ 11,22 ]; $scope.myArrayOfObjects = [{num: 101},{num: 202}] 在我们的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-repeat创建一个新的范围,它从父范围原型继承,但它还将项目的值分配给新子范围上的一个新属性。 (新属性的名称是循环变量的名称。)这里的ng-repeat的Angular源代码实际上是: childScope = scope.$new(); // child scope prototypically inherits from parent scope ... childScope[valueIdent] = value; // creates a new childScope property 如果item是一个基本类型(如在myArrayOfPrimitives中),则基本上将该值的副本分配给新的子范围属性。更改子作用域属性的值(即,使用ng-model,因此子作用域num)不会更改父作用域引用的数组。因此,在上面的第一个ng-repeat中,每个子范围获取一个独立于myArrayOfPrimitives数组的num属性: 这ng-repeat不会工作(像你想要的/期望它)。输入到文本框中会更改灰色框中的值,这些值仅在子范围中可见。我们想要的是输入影响myArrayOfPrimitives数组,而不是子scope的基元属性。要实现这一点,我们需要将模型更改为对象数组。 因此,如果item是一个对象,则对该新对象(非副本)的引用将分配给新的子scope。更改子作用域属性的值(即使用ng-model,因此obj.num)会更改父作用域引用的对象。因此,在上面的第二个ng-repeat中,我们有: (我上色一行灰色,以便它清楚它在哪里)。 这按预期工作。输入文本框将更改灰色框中的值,这些值对子范围和父范围都可见。 另见Difficulty with ng-model,ng-repeat,and inputs和 ng控制器 使用ng-controller的嵌套控制器导致正常的原型继承,就像ng-include和ng-switch一样,所以使用相同的技术。 (如果你真的想通过控制器范围继承来共享数据,那么你不需要做任何事情。子范围将有权访问所有的父范围属性。 指令 > default(scope:false) – 指令不创建一个新的作用域,所以这里没有继承。这很容易,但也很危险,因为,例如,一个指令可能认为它正在范围上创建一个新的属性,而事实上它正在破坏一个现有的属性。这不是编写用作可重用组件的指令的好选择。
和范围:{localProp:’@parentProp’}。必须使用属性来指定指令想要绑定到的每个父属性:
和范围:{localProp:’@theParentProp’}。
隔离作用域的__proto__引用对象。 Isolate scope的$ parent引用父范围,因此虽然它是孤立的,并且不从父范围继承原型,但它仍然是一个子范围。 对于下面的图片我们有 和 范围:{interpolatedProp:’@interpolated’,twowayBindingProp:’= twowayBinding’} 此外,假设指令在其链接函数中执行此操作:scope.someIsolateProp =“I’m isolated” 有关隔离范围的更多信息,请参阅 http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/ > transclude:true – 该伪指令创建一个新的“转录”子范围,它从父范围继承。被转换和被隔离的范围(如果有的话)是同级 – 每个范围的$ parent属性引用同一个父级范围。当转录和隔离范围都存在时,隔离范围属性$$ nextSibling将引用转录的范围。我不知道与转录范围有任何细微差别。 对于下面的图片,假设与上面相同的指令,添加:transclude:true 这个fiddle有一个showScope()函数,可用于检查隔离和转录的作用域。请参阅小提琴评论中的说明。 概要 有四种类型的范围: >正常原型范围继承 – ng-include,ng-switch,ng-controller,指令范围:true 对于所有范围(原型或非原型),Angular总是通过属性$ parent和$$ childHead和$$ childTail跟踪父子关系(即层次结构)。 图表是使用github上的graphviz“* .dot”文件生成的。Tim Caswell的“Learning JavaScript with Object Graphs”是使用GraphViz作为图表的灵感。
http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
本站文章除注明转载外,均为本站原创或编译 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |