gpt4 book ai didi

javascript - AngularJS 中范围原型(prototype)/原型(prototype)继承的细微差别是什么?

转载 作者:行者123 更新时间:2023-12-01 16:12:37 25 4
gpt4 key购买 nike

API Reference Scope page 说:

A scope can inherit from a parent scope.



Developer Guide Scope page 说:

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


  • 那么,子作用域是否总是原型(prototype)继承自其父作用域?
  • 有异常吗?
  • 确实继承的时候,总是正常的JavaScript原型(prototype)继承吗?
  • 最佳答案

    快速回答 :
    子作用域通常原型(prototype)继承自其父作用域,但并非总是如此。此规则的一个异常(exception)是带有 scope: { ... } 的指令——这会创建一个不典型继承的“隔离”范围。在创建“可重用组件”指令时经常使用此构造。

    至于细微差别,范围继承通常是直接的……直到您需要在子范围中使用 2 路数据绑定(bind) (即表单元素,ng-model)。如果您尝试从子作用域内部绑定(bind)到父作用域中的 原语 (例如,数字、字符串、 bool 值),则 Ng-repeat、ng-switch 和 ng-include 可能会绊倒您。它不像大多数人期望的那样工作。子作用域拥有自己的属性,可以隐藏/隐藏同名的父属性。您的解决方法是

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

  • 新的 AngularJS 开发者往往没有意识到 ng-repeatng-switchng-viewng-include 和 0x231414 都涉及到这些新的子作用域时常会出现问题,所以 0x231414s 经常会出现这些问题。 (有关问题的快速说明,请参阅 this example。)

    通过遵循 always have a '.' in your ng-models 的“最佳实践”——观看 3 分钟,可以轻松避免原语的这个问题。 Misko 演示了 ng-if 的原始绑定(bind)问题。

    有一个 '。'在您的模型中将确保原型(prototype)继承发挥作用。所以,使用
    <input type="text" ng-model="someObj.prop1">

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

    L-o-n-g 答案 :

    JavaScript 原型(prototype)继承

    也放在 AngularJS wiki 上: https://github.com/angular/angular.js/wiki/Understanding-Scopes

    首先对原型(prototype)继承有一个扎实的理解是很重要的,特别是如果您来自服务器端背景并且您更熟悉经典继承。所以让我们先回顾一下。

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

    prototypal inheritance

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

    如果我们尝试从子作用域访问定义在 parentScope 上的属性,JavaScript 将首先在子作用域中查找,未找到该属性,然后在继承的作用域中查找,并找到该属性。 (如果它没有在 parentScope 中找到该属性,它将继续沿原型(prototype)链向上……一直到根作用域)。所以,这些都是真的:
    childScope.aString === 'parent string'
    childScope.anArray[1] === 20
    childScope.anObject.property1 === 'parent prop1'
    childScope.aFunction() === 'parent output'

    假设我们然后这样做:
    childScope.aString = 'child string'

    不引用原型(prototype)链,在 childScope 中添加了一个新的 aString 属性。 这个新属性隐藏/隐藏了同名的 parentScope 属性。 当我们在下面讨论 ng-repeat 和 ng-include 时,这将变得非常重要。

    property hiding

    假设我们然后这样做:
    childScope.anArray[1] = '22'
    childScope.anObject.property1 = 'child prop1'

    引用原型(prototype)链是因为在 childScope 中找不到对象(anArray 和 anObject)。在 parentScope 中找到对象,并且在原始对象上更新属性值。 childScope 没有添加新的属性;没有创建新对象。 (请注意,在 JavaScript 中数组和函数也是对象。)

    follow the prototype chain

    假设我们然后这样做:
    childScope.anArray = [100, 555]
    childScope.anObject = { name: 'Mark', country: 'USA' }

    不咨询原型(prototype)链,子作用域获得两个新的对象属性,它们隐藏/隐藏具有相同名称的父作用域对象属性。

    more property hiding

    要点:
  • 如果我们读取childScope.propertyX,并且childScope有propertyX,那么就没有查询原型(prototype)链。
  • 如果我们设置childScope.propertyX,则不引用原型(prototype)链。

  • 最后一个场景:
    delete childScope.anArray
    childScope.anArray[1] === 22 // true

    我们先删除了 childScope 属性,然后当我们再次尝试访问该属性时,会引用原型(prototype)链。

    after removing a child property

    Angular 范围继承

    参赛者:
  • 以下创建新的作用域,并继承原型(prototype):ng-repeat、ng-include、ng-switch、ng-controller、指令 anArray、指令 scope: true
  • 下面创建了一个不继承原型(prototype)的新作用域:指令为 transclude: true 。这会创建一个“隔离”范围。

  • 请注意,默认情况下,指令不会创建新范围——即,默认值为 scope: { ... }

    ng-包含

    假设我们的 Controller 中有:
    $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 都会生成一个新的子作用域,它通常从父作用域继承。

    ng-include child scopes

    在第一个输入文本框中键入(例如“77”)会导致子作用域获得一个新的 scope: false 作用域属性,该属性隐藏/隐藏同名的父作用域属性。这可能不是您想要/期望的。

    ng-include with a primitive

    在第二个输入文本框中键入(例如“99”)不会产生新的子属性。因为 tpl2.html 将模型绑定(bind)到一个对象属性,当 ngModel 寻找对象 myObject 时,原型(prototype)继承就会启动——它在父作用域中找到它。

    ng-include with an object

    如果我们不想将模型从原始模型更改为对象,我们可以重写第一个模板以使用 $parent:
    <input ng-model="$parent.myPrimitive">

    在此输入文本框中键入(例如“22”)不会产生新的子属性。该模型现在绑定(bind)到父作用域的属性(因为 $parent 是引用父作用域的子作用域属性)。

    ng-include with $parent

    对于所有范围(原型(prototype)与否),Angular 始终通过范围属性 $parent、$$childHead 和 $$childTail 跟踪父子关系(即层次结构)。我通常不会在图表中显示这些范围属性。

    对于不涉及表单元素的场景,另一种解决方案是在父作用域上定义一个函数来修改原语。然后确保子级始终调用此函数,由于原型(prototype)继承,该函数可用于子级作用域。例如。,
    // in the parent scope
    $scope.setMyPrimitive = function(value) {
    $scope.myPrimitive = value;
    }

    这是一个使用这种“父函数”方法的 sample fiddle。 ( fiddle 是作为这个答案的一部分编写的: https://stackoverflow.com/a/14104318/215945 。)

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

    ng-switch

    ng-switch 作用域继承就像 ng-include 一样工作。因此,如果您需要对父作用域中的原语进行 2 向数据绑定(bind),请使用 $parent,或者将模型更改为对象,然后绑定(bind)到该对象的属性。这将避免父范围属性的子范围隐藏/阴影。

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

    ng-重复

    Ng-repeat 的工作方式略有不同。假设我们的 Controller 中有:
    $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,因此子作用域 myPrimitive )不会 而不是 更改父作用域引用的数组。因此,在上面的第一个 ng-repeat 中,每个子作用域都获得一个独立于 myArrayOfPrimitives 数组的 num 属性:

    ng-repeat with primitives

    这个 ng-repeat 不起作用(就像你想要/期望的那样)。在文本框中键入会更改灰色框中的值,这些值仅在子作用域中可见。我们想要的是输入影响 myArrayOfPrimitives 数组,而不是子范围的原始属性。为此,我们需要将模型更改为对象数组。

    因此,如果 item 是一个对象,则将对原始对象(而不是副本)的引用分配给新的子作用域属性。更改子作用域属性的值(即,使用 ng-model,因此 num ) 是否 更改父作用域引用的对象。所以在上面的第二个 ng-repeat 中,我们有:

    ng-repeat with objects

    (我将一条线涂成灰色,以便清楚它的去向。)

    这按预期工作。在文本框中键入会更改灰色框中的值,这些值对子作用域和父作用域都是可见的。

    另见 Difficulty with ng-model, ng-repeat, and inputs
    https://stackoverflow.com/a/13782671/215945

    ng- Controller

    使用 ng-controller 嵌套 Controller 会导致正常的原型(prototype)继承,就像 ng-include 和 ng-switch 一样,因此应用相同的技术。
    但是,“两个 Controller 通过 $scope 继承共享信息被认为是不好的形式”—— http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/
    应该使用服务在 Controller 之间共享数据。

    (如果您真的想通过 Controller 作用域继承共享数据,则无需执行任何操作。子作用域将可以访问所有父作用域属性。
    另见 Controller load order differs when loading or navigating )

    指令
  • default ( obj.num ) - 该指令不会创建新的作用域,因此这里没有继承。这很容易,但也很危险,因为例如,指令可能认为它正在范围内创建一个新属性,而实际上它正在破坏现有属性。这不是编写旨在作为可重用组件的指令的好选择。
  • scope: false - 该指令创建一个新的子作用域,该子作用域原型(prototype)继承自父作用域。如果多个指令(在同一个 DOM 元素上)请求一个新的作用域,则只会创建一个新的子作用域。由于我们有“正常”的原型(prototype)继承,这就像 ng-include 和 ng-switch,所以要警惕 2-way 数据绑定(bind)到父作用域原语,以及父作用域属性的子作用域隐藏/阴影。
  • scope: true - 该指令创建一个新的隔离/隔离范围。它不典型地继承。这通常是创建可重用组件时的最佳选择,因为指令不会意外读取或修改父作用域。但是,此类指令通常需要访问一些父作用域属性。对象哈希用于在父作用域和隔离作用域之间设置双向绑定(bind)(使用“=”)或单向绑定(bind)(使用“@”)。还有 '&' 可以绑定(bind)到父作用域表达式。因此,这些都创建了从父作用域派生的局部作用域属性。
    请注意,属性用于帮助设置绑定(bind)——您不能只在对象哈希中引用父作用域属性名称,您必须使用属性。例如,如果您想绑定(bind)到隔离范围内的父属性 scope: { ... } ,这将不起作用: parentProp<div my-directive> 。必须使用属性来指定指令要绑定(bind)到的每个父属性: scope: { localProp: '@parentProp' }<div my-directive the-Parent-Prop=parentProp>
    隔离范围的 scope: { localProp: '@theParentProp' } 引用对象。
    Isolate 作用域的 $parent 引用了父作用域,因此虽然它是隔离的并且不典型地从父作用域继承,但它仍然是一个子作用域。
    对于下面的图片,我们有
    __proto__
    <my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">此外,假设指令在其链接函数中执行此操作:scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' } isolated scope
    有关隔离范围的更多信息,请参阅 http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
  • scope.someIsolateProp = "I'm isolated" - 该指令创建了一个新的“transcluded”子作用域,其原型(prototype)继承自父作用域。嵌入的和隔离的作用域(如果有)是同级的——每个作用域的 $parent 属性引用相同的父作用域。当一个被嵌入的和一个隔离的作用域都存在时,隔离作用域属性 $$nextSibling 将引用被嵌入的作用域。我不知道嵌入范围的任何细微差别。
    对于下图,假设与上面相同的指令加上这个:transclude: true transcluded scope

  • fiddle 具有 transclude: true 函数,可用于检查隔离和嵌入范围。请参阅 fiddle 注释中的说明。

    概括

    有四种类型的范围:
  • 正常原型(prototype)范围继承——ng-include、ng-switch、ng-controller、指令与 showScope()
  • 带有复制/赋值的普通原型(prototype)范围继承——ng-repeat。 ng-repeat 的每次迭代都会创建一个新的子作用域,并且这个新的子作用域总是得到一个新的属性。
  • 隔离范围 - 指令与 scope: true 。这不是原型(prototype),但 '='、'@' 和 '&' 提供了一种通过属性访问父作用域属性的机制。
  • 内嵌作用域——带有 scope: {...} 的指令。这也是正常的原型(prototype)范围继承,但它也是任何隔离范围的同级。

  • 对于所有范围(原型(prototype)与否),Angular 始终通过属性 $parent 和 $$childHead 以及 $$childTail 跟踪父子关系(即层次结构)。

    图表是使用 "*.dot"文件生成的,这些文件位于 github 上。 Tim Caswell 的“ Learning JavaScript with Object Graphs”是使用 GraphViz 绘制图表的灵感。

    关于javascript - AngularJS 中范围原型(prototype)/原型(prototype)继承的细微差别是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14049480/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com