API 참조 범위 페이지는 말한다 :
범위 는 상위 범위에서 상속 할 수 있습니다 .
개발자 가이드의 범위 페이지는 말한다 :
범위는 (시제품 적으로) 부모 범위에서 속성을 상속합니다.
- 그렇다면 자식 범위는 항상 부모 범위에서 프로토 타입으로 상속됩니까?
- 예외가 있습니까?
- 상속 할 때 항상 정상적인 JavaScript 프로토 타입 상속입니까?
API 참조 범위 페이지는 말한다 :
범위 는 상위 범위에서 상속 할 수 있습니다 .
개발자 가이드의 범위 페이지는 말한다 :
범위는 (시제품 적으로) 부모 범위에서 속성을 상속합니다.
답변:
빠른 답변 :
자식 범위는 일반적으로 부모 범위에서 프로토 타입으로 상속되지만 항상 그런 것은 아닙니다. 이 규칙에 대한 한 가지 예외는 다음과 같은 지시문입니다 scope: { ... }
. 이것은 프로토 타입으로 상속되지 않는 "격리"범위를 만듭니다. 이 구성은 "재사용 가능한 구성 요소"지시문을 작성할 때 종종 사용됩니다.
뉘앙스와 관련하여 범위 상속은 일반적으로 간단합니다 ... 자식 범위에서 양방향 데이터 바인딩 (예 : 양식 요소, ng-model) 이 필요할 때까지 . 하위 범위 내에서 상위 범위 의 기본 (예 : 숫자, 문자열, 부울)에 바인딩하려고하면 Ng-repeat, ng-switch 및 ng-include가 트립 될 수 있습니다 . 대부분의 사람들이 예상대로 작동하지 않습니다. 자식 범위는 같은 이름의 부모 속성을 숨기거나 가리는 자체 속성을 가져옵니다. 해결 방법은
새로운 AngularJS와 개발자는 그것을 깨닫지 못하고 ng-repeat
, ng-switch
, ng-view
, ng-include
및 ng-if
이러한 지침이 관련 될 때 문제가 자주 보여줍니다 그래서 모두가 새 하위 범위를 만듭니다. ( 문제에 대한 간략한 설명은 이 예 를 참조하십시오 .)
항상 '.'을 갖는 "모범 사례"를 따르면 프리미티브의이 문제를 쉽게 피할 수 있습니다 . 당신의 ng-models에서 – 3 분 가치가있는 것을보십시오. Misko는와의 기본 바인딩 문제를 보여줍니다 ng-switch
.
가있는 '.' 모델에서 프로토 타입 상속이 작동하는지 확인합니다. 따라서 사용
<input type="text" ng-model="someObj.prop1">
<!--rather than
<input type="text" ng-model="prop1">`
-->
AngularJS 위키에도 있습니다 : 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'
오브젝트 (anArray 및 anObject)가 childScope에 없으므로 프로토 타입 체인을 참조하십시오. 개체는 parentScope에 있으며 속성 값은 원래 개체에서 업데이트됩니다. childScope에 새 속성이 추가되지 않습니다. 새로운 객체가 생성되지 않습니다. (자바 스크립트에서 배열과 함수도 객체입니다.)
그런 다음이 작업을 수행한다고 가정합니다.
childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }
프로토 타입 체인은 참조되지 않으며 자식 범위는 동일한 이름을 가진 parentScope 개체 속성을 숨기거나 음영 처리하는 두 가지 새로운 개체 속성을 얻습니다.
테이크 아웃 :
마지막 시나리오 :
delete childScope.anArray
childScope.anArray[1] === 22 // true
childScope 속성을 먼저 삭제 한 다음 속성에 다시 액세스하려고하면 프로토 타입 체인을 참조합니다.
도전자 :
scope: true
, 지시문 포함 transclude: true
.scope: { ... }
. 대신 "격리"범위가 생성됩니다.기본적으로 지시문은 새 범위를 만들지 않습니다 scope: false
. 즉, 기본값은 입니다.
컨트롤러에 있다고 가정 해보십시오.
$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
동일한 이름의 상위 범위 속성을 숨기거나 음영 처리 하는 새로운 범위 속성 을 가져옵니다 . 이것은 아마도 당신이 원하거나 기대하는 것이 아닙니다.
두 번째 입력 텍스트 상자에 입력 (예 : "99")해도 새 자식 속성이 생성되지 않습니다. tpl2.html은 모델을 객체 속성에 바인딩하므로 프로토 타입 상속은 ngModel이 객체 myObject를 찾을 때 시작됩니다. 상위 범위에서 찾습니다.
모델을 기본에서 객체로 변경하지 않으려는 경우 $ parent를 사용하도록 첫 번째 템플릿을 다시 작성할 수 있습니다.
<input ng-model="$parent.myPrimitive">
이 입력 텍스트 상자에 입력 (예 : "22")해도 새로운 자식 속성이 생성되지 않습니다. 모델은 이제 부모 범위의 속성에 바인딩됩니다 ($ parent는 부모 범위를 참조하는 자식 범위 속성이므로).
모든 범위 (prototypal 또는 not)에 대해 Angular는 범위 속성 $ parent, $$ childHead 및 $$ childTail을 통해 항상 부모-자식 관계 (예 : 계층 구조)를 추적합니다. 일반적으로 다이어그램에 이러한 범위 속성을 표시하지 않습니다.
양식 요소가 관련되지 않은 시나리오의 경우 다른 솔루션은 상위 범위에서 함수를 정의하여 기본 요소를 수정하는 것입니다. 그런 다음 자식이 항상이 함수를 호출하는지 확인하십시오.이 함수는 프로토 타입 상속으로 인해 자식 범위에서 사용할 수 있습니다. 예 :
// 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- 스위치 범위 상속은 ng-include와 동일하게 작동합니다. 따라서 부모 범위의 프리미티브에 양방향 데이터 바인딩이 필요한 경우 $ parent를 사용하거나 모델을 객체로 변경 한 다음 해당 객체의 속성에 바인딩합니다. 이렇게하면 자식 범위 숨기기 / 부모 범위 속성이 숨겨지지 않습니다.
스위치 케이스의 바인드 범위 인 AngularJS 도 참조하십시오 .
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에서 각 자식 범위는 num
myArrayOfPrimitives 배열과 독립적 인 속성을 가져 옵니다.
이 ng-repeat는 작동하지 않습니다 (원하는대로). 텍스트 상자에 입력하면 회색 상자의 값이 변경되어 하위 범위에서만 볼 수 있습니다. 우리가 원하는 것은 입력이 자식 범위 기본 속성이 아닌 myArrayOfPrimitives 배열에 영향을 미치는 것입니다. 이를 위해 모델을 객체 배열로 변경해야합니다.
따라서 item이 객체 인 경우 사본이 아닌 원본 객체에 대한 참조가 새 자식 범위 속성에 할당됩니다. 자식 범위 속성 값을 변경하면 (즉, ng-model 사용 obj.num
) 부모 범위가 참조하는 개체 가 변경됩니다. 위의 두 번째 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"
transclude: true
-지시문은 부모 범위에서 프로토 타입으로 상속되는 새로운 "번역 된"하위 범위를 작성합니다. 변환 된 범위와 격리 된 범위 (있는 경우)는 형제입니다. 각 범위의 $ parent 속성은 동일한 부모 범위를 참조합니다. 포함 된 범위와 격리 범위가 모두 존재하면 격리 범위 속성 $$ nextSibling은 포함 된 범위를 참조합니다. 나는 포함 된 범위의 뉘앙스를 알지 못합니다.
transclude: true
이 바이올린 에는 showScope()
격리 및 포함 된 범위를 검사하는 데 사용할 수 있는 기능이 있습니다. 바이올린 주석의 지침을 참조하십시오.
범위에는 네 가지 유형이 있습니다.
scope: true
scope: {...}
. 이것은 프로토 타입이 아니지만 '=', '@'및 '&'는 속성을 통해 상위 범위 속성에 액세스하는 메커니즘을 제공합니다.transclude: true
. 이것은 또한 일반적인 프로토 타입 범위 상속이지만 격리 범위의 형제이기도합니다.프로토 타입이든 아니든 모든 범위에 대해 Angular는 항상 $ parent 및 $$ childHead 및 $$ childTail 속성을 통해 부모-자식 관계 (예 : 계층)를 추적합니다.
다이어그램은 Graphviz에있는 "* .DOT"파일, github에 . Tim Caswell의 " 오브젝트 그래프로 JavaScript 학습 "은 다이어그램에 GraphViz를 사용하는 데 영감을주었습니다.
__proto__
참조 오브젝트를 분리하십시오 ." 대신 "범위의 __proto__
참조를 스코프 객체 분리"로 지정해야 합니다. 따라서 마지막 두 그림에서 주황색 "개체"상자는 "범위"상자 여야합니다.
Mark의 답변과 경쟁하고 싶지는 않지만 Javascript 상속 및 프로토 타입 체인에 익숙하지 않은 모든 사람을 마침내 클릭하게 만든 부분을 강조하고 싶었습니다 .
속성 만 쓰기가 아닌 프로토 타입 체인을 검색합니다. 그래서 당신이 설정할 때
myObject.prop = '123';
체인을 찾지 않지만 설정하면
myObject.myThing.prop = '123';
해당 쓰기 작업 내에서 미묘한 읽기가 진행되어 소품에 쓰기 전에 myThing을 조회합니다. 따라서 자식에서 object.properties에 쓰는 것이 부모의 개체에 도달하는 이유입니다.
@Scott Driscoll 답변에 자바 스크립트로 프로토 타입 상속의 예를 추가하고 싶습니다. 우리는 EcmaScript 5 사양의 일부인 Object.create ()와 함께 고전적인 상속 패턴을 사용할 것입니다.
먼저 "부모"객체 함수를 만듭니다
function Parent(){
}
그런 다음 "부모"개체 함수에 프로토 타입을 추가하십시오.
Parent.prototype = {
primitive : 1,
object : {
one : 1
}
}
"자식"객체 함수 생성
function Child(){
}
자식 프로토 타입 할당 (자식 프로토 타입을 부모 프로토 타입에서 상속하게 함)
Child.prototype = Object.create(Parent.prototype);
적절한 "자식"프로토 타입 생성자 할당
Child.prototype.constructor = Child;
자식 프로토 타입에 "changeProps"메소드를 추가하면 자식 개체의 "기본"속성 값을 다시 작성하고 자식 개체와 부모 개체의 "object.one"값을 변경합니다
Child.prototype.changeProps = function(){
this.primitive = 2;
this.object.one = 2;
};
부모 (아빠) 및 자식 (아들) 개체를 시작합니다.
var dad = new Parent();
var son = new Child();
하위 (아들) changeProps 메소드 호출
son.changeProps();
결과를 확인하십시오.
부모 기본 속성이 변경되지 않았습니다
console.log(dad.primitive); /* 1 */
자식 기본 속성이 변경됨 (재 작성 됨)
console.log(son.primitive); /* 2 */
부모 및 자식 개체 하나의 속성이 변경됨
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