Страница « Область действия API» гласит:
Область может наследоваться от родительской области.
На странице Scope Guide Developer говорится:
Область (прототипически) наследует свойства от своей родительской области.
- Итак, всегда ли дочерняя область прототипически наследуется от родительской области?
- Есть исключения?
- Когда это наследуется, всегда ли это нормальное наследование прототипа JavaScript?
Ответы:
Быстрый ответ :
дочерняя область обычно прототипно наследуется от своей родительской области, но не всегда. Единственным исключением из этого правила является директива с
scope: { ... }
- это создает «изолированную» область, которая не наследуется прототипами. Эта конструкция часто используется при создании директивы «повторно используемый компонент».Что касается нюансов, то наследование области обычно прямолинейно ... до тех пор, пока вам не понадобится двухстороннее связывание данных (т.е. элементы формы, ng-модель) в дочерней области. Ng-repeat, ng-switch и ng-include могут сбить вас с толку, если вы попытаетесь выполнить привязку к примитиву (например, число, строка, логическое значение) в родительской области изнутри дочерней области. Это не работает так, как большинство людей ожидает, что это должно работать. Дочерняя область действия получает свое собственное свойство, которое скрывает / скрывает родительское свойство с тем же именем. Ваши обходные пути
Новые разработчики AngularJS часто не понимают , что
ng-repeat
,ng-switch
,ng-view
,ng-include
иng-if
все это создает новые дочерние рамки, так что проблема часто появляется, когда эти директивы участвуют. (См. Этот пример для быстрой иллюстрации проблемы.)Эту проблему с примитивами можно легко избежать, следуя «лучшей практике» всегда иметь «.» в ваших ng-моделях - смотреть стоит 3 минуты. Миско демонстрирует проблему примитивного связывания с
ng-switch
.Иметь '.' в ваших моделях будет гарантировано, что наследование прототипа находится в игре. Итак, используйте
Длинный ответ :
Наследование прототипов JavaScript
Также размещен на вики AngularJS: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Важно сначала иметь четкое представление о наследовании прототипов, особенно если вы работаете на серверной основе и более знакомы с классическим наследованием. Итак, давайте сначала рассмотрим это.
Предположим, parentScope имеет свойства aString, aNumber, anArray, anObject и aFunction. Если childScope прототипически наследуется от parentScope, мы имеем:
(Обратите внимание, что для экономии места я показываю
anArray
объект как один синий объект с тремя значениями, а не как один синий объект с тремя отдельными литералами серого цвета.)Если мы попытаемся получить доступ к свойству, определенному в parentScope, из дочерней области, JavaScript сначала будет искать в дочерней области, а не находить свойство, затем искать в унаследованной области и находить свойство. (Если он не найдет свойство в parentScope, он продолжит цепочку прототипов ... вплоть до корневой области). Итак, все это правда:
Предположим, что мы тогда делаем это:
Цепочка прототипов не используется, и в childScope добавляется новое свойство aString. Это новое свойство скрывает / скрывает свойство parentScope с тем же именем. Это станет очень важным, когда мы обсудим ng-repeat и ng-include ниже.
Предположим, что мы тогда делаем это:
С цепочкой прототипов обращаются, потому что объекты (anArray и anObject) не найдены в childScope. Объекты находятся в parentScope, а значения свойств обновляются в исходных объектах. Новые свойства не добавляются в childScope; новые объекты не создаются. (Обратите внимание, что в JavaScript массивы и функции также являются объектами.)
Предположим, что мы тогда делаем это:
Цепочка прототипов не используется, и дочерняя область получает два новых свойства объекта, которые скрывают / скрывают свойства объекта parentScope с одинаковыми именами.
Takeaways:
Последний сценарий:
Сначала мы удалили свойство childScope, а затем при повторном обращении к свойству просматриваем цепочку прототипов.
Угловое наследование
Претенденты:
scope: true
, директива withtransclude: true
.scope: { ... }
. Это создает "изолировать" область вместо этого.Обратите внимание, что по умолчанию директивы не создают новую область видимости, то есть по умолчанию
scope: false
.нг-включают
Предположим, у нас есть в нашем контроллере:
И в нашем HTML:
Каждый ng-include генерирует новую дочернюю область, которая прототипно наследуется от родительской области.
Ввод (скажем, «77») в первом текстовом поле ввода приводит к тому, что дочерняя область получает новое
myPrimitive
свойство области, которое скрывает / скрывает родительское свойство области с тем же именем. Это, вероятно, не то, что вы хотите / ожидаете.Ввод (скажем, «99») во второе текстовое поле ввода не приводит к появлению нового дочернего свойства. Поскольку tpl2.html связывает модель со свойством объекта, наследование прототипа включается, когда ngModel ищет объект myObject - он находит его в родительской области видимости.
Мы можем переписать первый шаблон для использования $ parent, если мы не хотим менять нашу модель с примитива на объект:
Ввод (скажем, «22») в это текстовое поле ввода не приводит к появлению нового дочернего свойства. Модель теперь привязана к свойству родительской области (поскольку $ parent - это дочерняя область, которая ссылается на родительскую область).
Для всех областей (прототип или нет) Angular всегда отслеживает родительско-дочерние отношения (т. Е. Иерархию) с помощью свойств области $ parent, $$ childHead и $$ childTail. Обычно я не показываю эти свойства области на диаграммах.
Для сценариев, в которых элементы формы не задействованы, другое решение состоит в том, чтобы определить функцию в родительской области видимости для изменения примитива. Затем убедитесь, что дочерний элемент всегда вызывает эту функцию, которая будет доступна для дочерней области благодаря наследованию прототипа. Например,
Вот пример скрипта, который использует этот подход «родительская функция». (Скрипка была написана как часть этого ответа: https://stackoverflow.com/a/14104318/215945 .)
См. Также https://stackoverflow.com/a/13782671/215945 и https://github.com/angular/angular.js/issues/1267 .
нг-переключатель
Наследование области действия ng-switch работает так же, как ng-include. Поэтому, если вам требуется двусторонняя привязка данных к примитиву в родительской области, используйте $ parent или измените модель на объект, а затем привяжите к свойству этого объекта. Это позволит избежать скрытия / теневого копирования дочерних областей свойств родительских областей.
См. Также AngularJS, связать область применения коммутатора?
нг-повтор
Нг-повтор работает немного по-другому. Предположим, у нас есть в нашем контроллере:
И в нашем HTML:
Для каждого элемента / итерации ng-repeat создает новую область, которая прототипически наследуется от родительской области, но также назначает значение элемента новому свойству в новой дочерней области . (Имя нового свойства - это имя переменной цикла.) Вот что на самом деле представляет исходный код Angular для ng-repeat:
Если элемент является примитивом (как в myArrayOfPrimitives), по сути, копия значения присваивается новому дочернему свойству области. Изменение значения свойства дочерней области (т. Е. Использование ng-модели, следовательно, дочерней области
num
) не изменяет массив, на который ссылается родительская область. Таким образом, в первом вышеприведенном ng-повторе каждаяnum
дочерняя область получает свойство, которое не зависит от массива myArrayOfPrimitives:Это ng-repeat не будет работать (как вы хотите / ожидаете). Ввод в текстовые поля изменяет значения в серых полях, которые видны только в дочерних областях. Мы хотим, чтобы входные данные влияли на массив myArrayOfPrimitives, а не на дочернее примитивное свойство области видимости. Для этого нам нужно изменить модель на массив объектов.
Таким образом, если элемент является объектом, ссылка на исходный объект (не на копию) назначается новому дочернему свойству области. Изменение значения свойства дочерней области (т. Е. С помощью ng-модели, следовательно
obj.num
) действительно изменяет объект, на который ссылается родительская область. Итак, во втором нг-повторе выше мы имеем:(Я нарисовал одну линию серым, чтобы было ясно, куда она идет.)
Это работает как ожидалось. Ввод в текстовые поля изменяет значения в серых полях, которые видны как для дочерней, так и для родительской областей.
См. Также Трудности с ng-моделью, ng-repeat и входными данными и https://stackoverflow.com/a/13782671/215945
нг-контроллер
Вложение контроллеров с использованием ng-controller приводит к нормальному наследованию прототипов, так же, как ng-include и ng-switch, поэтому применяются те же методы. Однако «считается плохой формой для двух контроллеров обмениваться информацией через наследование $ scope» - http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ Для обмена данными между службами следует использовать службу контроллеры вместо.
(Если вы действительно хотите обмениваться данными через наследование области контроллера, вам ничего не нужно делать. Дочерняя область будет иметь доступ ко всем свойствам родительской области. См. Также Порядок загрузки контроллера отличается при загрузке или навигации )
директивы
scope: false
) - директива не создает новую область видимости, поэтому здесь нет наследования. Это легко, но также и опасно, потому что, например, директива может подумать, что создает новое свойство в области действия, когда фактически забивает существующее свойство. Это не очень хороший выбор для написания директив, предназначенных для повторного использования.scope: true
- директива создает новую дочернюю область, которая прототипно наследуется от родительской области. Если более одной директивы (для одного элемента DOM) запрашивает новую область, создается только одна новая дочерняя область. Так как у нас есть «нормальное» наследование прототипов, это похоже на ng-include и ng-switch, так что будьте осторожны с двухсторонней привязкой данных к родительским областям примитивов и дочерним областям скрытия / теневого копирования свойств родительских областей.scope: { ... }
- директива создает новую изолированную / изолированную область. Он не наследуется по прототипу. Обычно это лучший выбор при создании повторно используемых компонентов, поскольку директива не может случайно прочитать или изменить родительскую область видимости. Однако таким директивам часто требуется доступ к нескольким родительским свойствам области. Хэш объекта используется для установки двусторонней привязки (с помощью «=») или односторонней привязки (с помощью «@») между родительской областью и областью изолята. Также есть '&' для привязки к родительским выражениям области. Таким образом, все они создают локальные свойства области, которые являются производными от родительской области. Обратите внимание, что атрибуты используются для настройки привязки - вы не можете просто ссылаться на имена свойств родительской области в хэше объекта, вы должны использовать атрибут. Например, это не будет работать, если вы хотите привязать к родительскому свойствуparentProp
в изолированном объеме:<div my-directive>
аscope: { localProp: '@parentProp' }
. Атрибут должен использоваться для указания каждого родительского свойства, с которым директива хочет связать:<div my-directive the-Parent-Prop=parentProp>
иscope: { localProp: '@theParentProp' }
.Изолировать
__proto__
ссылки области видимости объекта. Изолирующая область $ parent ссылается на родительскую область, поэтому, хотя она изолирована и не наследуется прототипно от родительской области, она все же является дочерней областью.Для рисунка ниже мы также
<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
иscope: { 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
Эта скрипка имеет
showScope()
функцию, которую можно использовать для проверки изолированного и включенного объема. Смотрите инструкции в комментариях в скрипке.Резюме
Существует четыре типа областей:
scope: true
scope: {...}
. Это не прототип, но '=', '@' и '&' предоставляют механизм доступа к свойствам родительской области через атрибуты.transclude: true
. Этот тип также является обычным наследованием прототипной области, но он также является родственным элементом любой изолированной области.Для всех областей (прототип или нет) Angular всегда отслеживает отношения родитель-потомок (то есть иерархию) через свойства $ parent и $$ childHead и $$ childTail.
Диаграммы были сгенерированы с GraphvizФайлы * .dot, которые находятся на github . « Изучение JavaScript с помощью графов объектов » Тима Касвелла послужило вдохновением для использования GraphViz для диаграмм.
источник
__proto__
ссылки области видимости объекта». вместо этого должно быть «Изолировать__proto__
ссылки на область видимости объекта Scope». Таким образом, на последних двух рисунках оранжевые поля «Объект» должны вместо этого быть «областями видимости».Я ни в коем случае не хочу конкурировать с ответом Марка, но просто хотел выделить часть, которая, в конце концов, заставила все щелкнуть как новичка в наследовании Javascript и его цепочке прототипов .
Только свойство читает поиск по цепочке прототипов, а не пишет. Итак, когда вы установите
Это не ищет цепочку, но когда вы установите
внутри этой операции записи происходит тонкое чтение, которое пытается найти myThing перед записью в свой реквизит. Так вот почему запись в object.properties от потомка получает объекты родителя.
источник
Я хотел бы добавить пример прототипического наследования с javascript к ответу @Scott Driscoll. Мы будем использовать классический шаблон наследования с Object.create (), который является частью спецификации EcmaScript 5.
Сначала мы создаем объектную функцию «Родитель»
Затем добавьте прототип в объектную функцию «Родитель»
Создать функцию объекта «Дочерний»
Назначить дочерний прототип (сделать дочерний прототип наследуемым от родительского прототипа)
Назначьте правильный конструктор-прототип "Child"
Добавьте метод "changeProps" к дочернему прототипу, который перезапишет значение свойства "примитив" в дочернем объекте и изменит значение "object.one" как в дочернем, так и в родительском объектах.
Инициировать объекты Parent (папа) и Child (сын).
Вызовите метод ChildPro (сын) changeProps
Проверьте результаты.
Родительское примитивное свойство не изменилось
Свойство дочернего примитива изменено (переписано)
Свойства родительского и дочернего объекта.one изменены
Рабочий пример здесь http://jsbin.com/xexurukiso/1/edit/
Дополнительная информация о Object.create здесь https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create
источник