질문 : AngularJS에서 범위 프로토 타입 / 프로토 타입 상속의 뉘앙스는 무엇입니까?
API 참조 범위 페이지에 다음 내용이 표시됩니다.
범위는 상위 범위에서 상속 할 수 있습니다.
개발자 가이드 범위 페이지 에는 다음과 같은 내용이 있습니다.
범위 (원형)는 부모 범위에서 속성을 상속합니다.
- 그렇다면 자식 범위는 항상 부모 범위에서 프로토 타입 적으로 상속됩니까?
- 예외가 있습니까?
- 상속 할 때 항상 일반적인 JavaScript 프로토 타입 상속입니까?
답변
빠른 답변 :
일반적으로 자식 범위는 프로토 타입 적으로 부모 범위에서 상속되지만 항상 그런 것은 아닙니다. 이 규칙에 대한 한 가지 예외는 scope: { ... }
지시문입니다.이 경우 프로토 타입 적으로 상속되지 않는 "격리"범위가 생성됩니다. 이 구조는 "재사용 가능한 구성 요소"지시문을 만들 때 자주 사용됩니다.
뉘앙스와 관련하여, 범위 상속은 일반적으로 간단합니다 ... 하위 범위에서 양방향 데이터 바인딩 (즉, 양식 요소, ng-model)이 필요할 때까지. Ng-repeat, ng-switch 및 ng-include는 자식 범위 내에서 부모 범위의 기본 요소 (예 : 숫자, 문자열, 부울)에 바인딩하려고하면 문제가 발생할 수 있습니다. 대부분의 사람들이 예상하는 방식으로 작동하지 않습니다. 자식 범위는 같은 이름의 부모 속성을 숨기거나 숨기는 자체 속성을 가져옵니다. 해결 방법은 다음과 같습니다.
ng-repeat
, ng-switch
, ng-view
, ng-include
및 ng-if
모두가 새 하위 범위를 생성한다는 사실을 인식하지 못하므로 이러한 지시문이 관련 될 때 문제가 종종 나타납니다. (문제에 대한 간략한 설명은 이 예 를 참조하십시오.)
프리미티브와 관련된이 문제는 항상 '.'를 갖는 "모범 사례"를 따르면 쉽게 피할 수 있습니다. 당신의 ng-models에서 – 3 분 분량을보십시오. 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가 prototypically 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에 새 속성이 추가되지 않습니다. 새 개체가 생성되지 않습니다. (JavaScript에서 배열과 함수도 객체입니다.)
그런 다음 이렇게한다고 가정합니다.
childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }
프로토 타입 체인은 참조되지 않으며 자식 범위는 동일한 이름의 parentScope 개체 속성을 숨기거나 숨기는 두 개의 새 개체 속성을 가져옵니다.
요약 :
- childScope.propertyX를 읽고 childScope에 propertyX가있는 경우 프로토 타입 체인은 참조되지 않습니다.
- childScope.propertyX를 설정하면 프로토 타입 체인이 참조되지 않습니다.
마지막 시나리오 :
delete childScope.anArray
childScope.anArray[1] === 22 // true
먼저 childScope 속성을 삭제 한 다음 속성에 다시 액세스하려고 할 때 프로토 타입 체인을 참조합니다.
경쟁자 :
- 다음은 새 범위를 만들고 프로토 타입 적으로 상속합니다 : ng-repeat, ng-include, ng-switch, ng-controller, directive with
scope: true
, directive withtransclude: 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는 부모 범위를 참조하는 자식 범위 속성이기 때문).
모든 범위 (프로토 티팔 여부)에 대해 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-switch 범위 상속은 ng-include처럼 작동합니다. 따라서 부모 범위의 기본 요소에 양방향 데이터 바인딩이 필요한 경우 $ parent를 사용하거나 모델을 개체로 변경 한 다음 해당 개체의 속성에 바인딩합니다. 이렇게하면 부모 범위 속성의 자식 범위 숨기기 / 섀도 잉을 방지 할 수 있습니다.
AngularJS, bind scope of a switch-case 도 참조하십시오.
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
항목이 기본 (myArrayOfPrimitives에서와 같이)이면 기본적으로 값의 사본이 새 하위 범위 속성에 할당됩니다. 자식 범위 속성의 값을 변경 (즉, ng-model 사용하여 자식 범위 num
)해도 부모 범위가 참조하는 배열은 변경 되지 않습니다. 따라서 위의 첫 번째 ng-repeat에서 각 자식 범위는 myArrayOfPrimitives 배열과 독립적 인 num
이 ng-repeat는 작동하지 않습니다 (원하거나 기대하는 것처럼). 텍스트 상자에 입력하면 회색 상자의 값이 변경되며 하위 범위에서만 볼 수 있습니다. 우리가 원하는 것은 입력이 자식 범위 기본 속성이 아니라 myArrayOfPrimitives 배열에 영향을주는 것입니다. 이를 수행하려면 모델을 객체 배열로 변경해야합니다.
따라서 항목이 개체 인 경우 복사본이 아닌 원본 개체에 대한 참조가 새 자식 범위 속성에 할당됩니다. 자식 범위 속성의 값을 변경하면 (즉, ng-model 사용하여 obj.num
) 부모 범위가 참조하는 개체 가 변경됩니다. 따라서 위의 두 번째 ng-repeat에는 다음이 있습니다.
(나는 어디로 가는지 명확하게하기 위해 한 줄을 회색으로 칠했습니다.)
이것은 예상대로 작동합니다. 텍스트 상자에 입력하면 회색 상자의 값이 변경되어 자식 및 부모 범위 모두에 표시됩니다.
ng-model, ng-repeat 및 입력의 어려움 과 https://stackoverflow.com/a/13782671/215945도 참조하십시오.
ng-controller를 사용하여 컨트롤러를 중첩하면 ng-include 및 ng-switch와 마찬가지로 일반적인 프로토 타입 상속이 이루어 지므로 동일한 기술이 적용됩니다. 그러나 "두 컨트롤러가 $ scope 상속을 통해 정보를 공유하는 것은 나쁜 형태로 간주됩니다." -http ://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ 데이터를 공유하려면 서비스를 사용해야합니다. 대신 컨트롤러.
(컨트롤러 범위 상속을 통해 데이터를 공유하려는 경우 수행 할 작업이 없습니다. 하위 범위는 모든 상위 범위 속성에 액세스 할 수 있습니다. 로드 또는 탐색 할 때 컨트롤러로드 순서가 다름 참조)
이 바이올린 에는 격리 및 제외 된 범위를 검사하는 데 사용할 수 showScope()
바이올린의 주석에있는 지침을 참조하십시오.
범위에는 네 가지 유형이 있습니다.
모든 범위 (프로토 티팔 여부)에 대해 Angular는 속성 $ parent 및 $$ childHead 및 $$ childTail을 통해 항상 부모-자식 관계 (즉, 계층 구조)를 추적합니다.
다이어그램은 github에 있는 graphviz "* .dot"파일로 생성되었습니다. Tim Caswell의 " Learning JavaScript with Object Graphs "는 다이어그램에 GraphViz를 사용한 영감이었습니다.
출처 : https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
'프로그래밍 언어 > HTML,CSS,JS' 카테고리의 다른 글
JavaScript에서 스택과 큐를 구현하는 방법 (0) | 2021.07.27 |
---|---|
CSS를 통해 PNG 이미지의 색상을 변경하는 방법 (0) | 2021.07.23 |
HTML <base> 태그는 어떤 상황에서 활용할수 있을까? (0) | 2021.07.23 |
클래스 용 CSS의 와일드 카드 * (0) | 2021.07.23 |
Javascript에서 문자열의 첫 번째 문자 삭제 (0) | 2021.07.22 |