소개하다
이 장에서는 ECMAScript의 기능 기능으로 매개 변수를 전달하는 전략을 설명합니다.
컴퓨터 과학 에서이 전략은 일반적으로 "평가 전략"이라고 불립니다 (Uncle 's Note : 어떤 사람들은 그것이 평가 전략으로 번역 된 반면, 다른 사람들은 그것을 과제 전략으로 번역한다고 말합니다. 다음 내용을 살펴보면 과제 전략이라고 부르는 것이 더 적절하다고 생각합니다. 어쨌든 제목은 모든 사람이 이해하기 쉬운 평가 전략으로 작성되어야합니다). 예를 들어, 프로그래밍 언어에서 평가 또는 계산 표현식 규칙을 설정합니다. 매개 변수를 함수로 전달하기위한 전략은 특별한 경우입니다.
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
이 기사를 작성하는 이유는 포럼의 누군가가 매개 변수를 전달하기위한 몇 가지 전략을 정확하게 설명하도록 요청했기 때문입니다. 우리는 여기에 해당 정의를 제공하여 모든 사람에게 도움이되기를 바라고 있습니다.
많은 프로그래머는 JavaScript (일부 다른 언어에서도)에서 객체가 참조로 전달되는 반면 원래 값 유형은 값으로 전달된다고 확신합니다. 또한 많은 기사 가이 "사실"에 대해 이야기하지만 많은 사람들 이이 용어를 실제로 이해하고 몇 명이 맞습니까? 이 기사에서 하나씩 설명하겠습니다.
일반 이론
과제 이론에는 일반적으로 두 가지 할당 전략이 있습니다. 엄격한 - 프로그램에 들어가기 전에 매개 변수가 계산된다는 것을 의미합니다. 비 스트릭 - 파라미터의 계산은 계산 요구 사항에 따라 계산된다는 것을 의미합니다 (즉, 계산 지연과 동일합니다).
그런 다음 여기에서는 기본 함수 매개 변수 전송 전략을 고려합니다.이 전략은 ECMAScript의 시작점에서 매우 중요합니다. 가장 먼저 주목해야 할 것은 엄격한 매개 변수 전달 전략이 ECMAScript (C, Java, Python 및 Ruby와 같은 다른 언어에서도)에서 사용된다는 것입니다.
또한 전달 된 매개 변수의 계산 순서도 매우 중요합니다.
엄격한 전송 전략도 여러 종자 전략으로 나뉘며,이 장에서 가장 중요한 전략에 대해 자세히 논의 할 것입니다.
아래에 논의 된 모든 전략이 ECMAScript에서 사용되는 것은 아니므로 이러한 전략의 특정 동작을 논의 할 때 의사 코드를 사용하여이를 보여주었습니다.
가치로 통과하십시오
가치를 높이면 많은 개발자들은 매개 변수의 값이 발신자가 전달하는 객체 값의 사본이라는 것을 잘 알고 있습니다. 함수 내부의 매개 변수 값을 변경하면 외부 객체에 영향을 미치지 않습니다 (매개 변수의 값은 외부에 있습니다). 일반적으로, 새로운 메모리가 재 할당됩니다 (우리는 할당 된 메모리가 구현되는 방법에주의를 기울이지 않습니다. 또한 스택 또는 동적 메모리 할당이기도합니다). 새 메모리 블록의 값은 외부 객체의 사본이며 그 값은 함수 내에서 사용됩니다.
코드 사본은 다음과 같습니다.
바 = 10
프로 시저 Foo (Bararg) :
Bararg = 20;
끝
Foo (바)
// foo 내부의 값을 변경하면 내부 막대의 값에 영향을 미치지 않습니다.
인쇄 (bar) // 10
그러나 함수의 매개 변수가 원래 값이 아니라 복잡한 구조 객체 인 경우 성능이 큰 문제를 일으킬 것입니다. C ++는이 문제가 있습니다. 구조를 함수로 값으로 전달할 때는 완전한 사본입니다.
일반적인 예를 들어 다음 과제 전략을 사용하여 테스트합시다. 2 개의 매개 변수를 허용하는 함수에 대해 생각해보십시오. 첫 번째 매개 변수는 객체의 값이고, 두 번째 매개 변수는 들어오는 객체가 완전히 수정되었는지 (객체에 객체를 재 할당) 객체의 일부 속성 만 수정하는 부울 마크입니다.
코드 사본은 다음과 같습니다.
// 참고 : 다음은 JS 구현이 아닌 모든 의사 코드입니다.
bar = {
x : 10,
Y : 20
}
절차 foo (Bararg, IsfullChange) :
IsfullChange :
Bararg = {z : 1, q : 2}
출구
끝
bararg.x = 100
Bararg.y = 200
끝
Foo (바)
// 값으로 통과하면 외부 객체가 변경되지 않습니다.
print (bar) // {x : 10, y : 20}
// 객체를 완전히 변경 (새 값 지정)
foo (바, 참)
// 변경 사항도 없습니다
print (bar) // {x : 10, y : 20}, {z : 1, q : 2} 대신}
참조로 통과하십시오
또 다른 잘 알려진 패스-참조는 값 사본이 아니라 외부의 객체의 직접 참조 주소와 같은 객체에 대한 암시적인 참조입니다. 함수 내부의 매개 변수에 대한 변경은 동일한 객체를 참조하기 때문에 함수 외부의 객체의 값에 영향을 미칩니다. 즉, 매개 변수는 외부 객체의 별칭과 동일하기 때문입니다.
의사 코드 :
코드 사본은 다음과 같습니다.
절차 foo (Bararg, IsfullChange) :
IsfullChange :
Bararg = {z : 1, q : 2}
출구
끝
bararg.x = 100
Bararg.y = 200
끝
// 위와 동일한 객체를 사용하십시오
bar = {
x : 10,
Y : 20
}
// 참조 별 호출 결과는 다음과 같습니다.
Foo (바)
// 객체의 속성 값이 변경되었습니다.
print (bar) // {x : 100, y : 200}
// 새 값을 재 할당하면 객체에도 영향을 미칩니다
foo (바, 참)
//이 객체는 이미 새로운 객체입니다
print (bar) // {z : 1, q : 2}
이 전략은 큰 속성을 가진 큰 구조 객체와 같은 복잡한 객체를보다 효율적으로 통과 할 수 있습니다.
공유하여 전화하십시오
모든 사람은 위의 두 가지 전략을 알고 있지만 여기서 이야기하고 싶은 전략을 알지 못할 수도 있습니다 (실제로는 학업 전략입니다). 그러나 우리는 이것이 ECMAScript의 매개 변수 전달 전략에서 중요한 역할을하는 전략임을 곧 알게 될 것입니다.
이 전략에는 "오브젝트를 통과"또는 "객체 공유를 통과"하는 동의어도 있습니다.
이 전략은 1974 년 CLU 프로그래밍 언어를 위해 Barbara Liskov가 제안했습니다.
이 전략의 핵심 요점은 함수가 객체의 사본 (사본)을 수신하고 참조 사본은 공식 매개 변수 및 해당 값과 관련이 있다는 것입니다.
함수에 의해 수신 된 매개 변수는 직접 객체 별명이 아니라 참조 주소의 사본이기 때문에 여기에 "Pass By Reference"에 표시되는 참조를 호출 할 수 없습니다.
가장 중요한 차이점은 함수 내부의 매개 변수에 새 값을 재 할당하는 것이 외부 객체에 영향을 미치지 않는다는 것입니다 (위의 예에서 참조로 전달 된 경우와 같이) 매개 변수는 주소 사본이므로 외부 및 내부에 액세스하는 동일한 개체는 동일한 개체입니다 (예 : 외부 객체는 값을 통과하려는 완전한 사본이 아닙니다). 매개 변수 객체의 속성 값을 변경하면 외부 객체에 영향을 미칩니다.
코드 사본은 다음과 같습니다.
절차 foo (Bararg, IsfullChange) :
IsfullChange :
Bararg = {z : 1, q : 2}
출구
끝
bararg.x = 100
Bararg.y = 200
끝
// 여전히이 객체 구조를 사용하십시오
bar = {
x : 10,
Y : 20
}
// 기여를 전달하면 객체에 영향을 미칩니다
Foo (바)
// 객체의 속성이 수정되었습니다
print (bar) // {x : 100, y : 200}
// 재 할당이 작동하지 않습니다
foo (바, 참)
// 여전히 위의 값입니다
print (bar) // {x : 100, y : 200}
이 처리의 가정은 대부분의 언어로 사용되는 객체가 원래 값이 아니라는 것입니다.
Pass By Share는 가치별로 통과하는 특별한 경우입니다.
공유로 제공하는 전략은 Java, ECMAScript, Python, Ruby, Visual Basic 등 다양한 언어로 사용됩니다. 또한 Python 커뮤니티는이 용어를 사용했으며 다른 언어로는 사람들이 혼란스러워하는 경향이 있기 때문에 다른 언어로 사용될 수 있습니다. Java, ECMAScript 또는 Visual Basic과 같은 대부분의 경우이 전략을 Pass -By -Value (의미 : 특수 값 - 참조 사본)라고도합니다.
한편으로는 다음과 같습니다. 함수로 전달 된 매개 변수는 바운드 값 (참조 주소)의 이름 일뿐 아니라 외부 객체에 영향을 미치지 않습니다.
다른 한편으로, 많은 포럼이 객체를 JavaScript 함수로 전달하는 방법에 대해 이야기하고 있기 때문에이 용어는 실제로 잘못 먹는 것으로 간주됩니다.
일반적으로 가치가 전달되는 일반적인 이론이 있습니다. 그러나 현재이 값은 우리가 주소 사본 (사본)이라고 부르는 것이므로 규칙을 중단하지 않습니다.
루비에서는이 전략을 참조로 Pass라고합니다. 다시 말씀 드리겠습니다. 그것은 큰 구조의 사본 (예 : 값이 아닌)의 사본으로 전달되지 않으며, 다른 한편으로는 원래 객체에 대한 참조를 처리하지 않으며 수정할 수 없습니다. 따라서이 단면 의이 개념은 더 혼란 스러울 수 있습니다.
이론적으로 가치가 전달되는 특별 사건과 같은 이론적으로 전달되는 특별한 사례는 없습니다.
그러나 위에서 언급 한 기술에서 이러한 전략을 여전히 이해해야합니다 (Java, Ecmascript, Python, Ruby, Other). 실제로, 그들이 사용하는 전략은 공유를 통과하는 것입니다.
공유 및 포인터를 누릅니다
с/с ++의 경우,이 전략은 포인터 값을 통과하는 것과 동일하지만 중요한 차이가 있습니다.이 전략은 포인터를 피하고 객체를 완전히 변경할 수 있습니다. 그러나 일반적으로 값 (주소) 포인터를 새 메모리 블록에 할당합니다 (즉, 이전에 참조 된 메모리 블록은 변경되지 않은 상태로 유지됨). 포인터를 통해 객체 속성을 변경하면 Adong의 외부 객체에 영향을 미칩니다.
따라서 포인터 카테고리는 이것이 주소 값으로 전달된다는 것을 분명히 알 수 있습니다. 이 경우, 공유를 통과하는 것은 포인터 할당 동작 (단위 회의가 아님)과 같은 "합성 설탕"이거나 참조와 같은 특성을 수정하는 것과 같은 단지 "합성 설탕"이거나, 때로는 "안전 포인터"라고 명명 될 수 있습니다.
그러나, с/с + +는 또한 명백한 포인터의 불균형없이 물체 특성을 참조 할 때 특수한 구문 설탕을 가지고 있습니다.
코드 사본은 다음과 같습니다.
(*obj) 대신 obj-> x
C ++와 가장 밀접한 관련이있는이 이데올로기는 "스마트 포인터"의 구현에서 볼 수 있습니다. 예를 들어, boost :: shared_ptr에서 할당 연산자와 복사 생성자가 과부하되고 객체의 참조 카운터도 GC를 통해 객체를 삭제하는 데 사용됩니다. 이 데이터 유형은 비슷한 이름 -share_ptr입니다.
ECMAScript 구현
이제 우리는 ecmascript에서 객체를 매개 변수로 전달하는 정책을 알고 있습니다 - 공유 : 매개 변수의 속성을 수정하면 외부에 영향을 미치고 값을 재 할당하면 외부 객체에 영향을 미치지 않습니다. 그러나 위에서 언급 한 바와 같이, ECMAScript 개발자는 일반적으로 값이 참조 주소의 사본이라는 점을 제외하고는 값을 통과합니다.
JavaScript Inventor Brendan Ashe는 다음과 같이 썼습니다. 통과 된 것은 참조 사본 (주소 사본)입니다. 포럼에서 언급 한 모든 사람들 도이 설명에 따라 옳습니다.
더 정확하게는,이 행동은 간단한 과제로 이해 될 수 있습니다. 우리는 내부가 완전히 다른 객체임을 알 수 있지만 동일한 값, 즉 주소 사본을 참조합니다.
ECMAScript 코드 :
코드 사본은 다음과 같습니다.
var foo = {x : 10, y : 20};
var bar = foo;
경고 (bar === foo); // 진실
bar.x = 100;
bar.y = 200;
경고 ([foo.x, foo.y]); // [100, 200]
즉, 두 개의 식별자 (이름 바인딩)는 메모리의 동일한 객체에 바인딩 되며이 개체를 공유합니다.
foo 값 : addr (0xff) => {x : 100, y : 200} (주소 0xff) <= 막대 값 : addr (0xff)
재 할당하기 위해 바인딩은 이전에 바인딩 된 객체에 영향을 미치지 않고 새로운 객체 식별자 (새 주소)입니다.
코드 사본은 다음과 같습니다.
bar = {z : 1, q : 2};
경고 ([foo.x, foo.y]); // [100, 200] 변화가 없습니다
경고 ([bar.z, bar.q]); // [1, 2]하지만 이제 참조는 새로운 개체입니다.
즉, Foo와 Bar는 이제 다른 값과 주소가 다릅니다.
코드 사본은 다음과 같습니다.
foo 값 : addr (0xff) => {x : 100, y : 200} (주소 0xff)
막대 값 : addr (0xfa) => {z : 1, q : 2} (주소 0xfa)
여기에 언급 된 객체의 값이 객체 구조 자체가 아닌 주소이며 변수를 다른 변수 (지정된 값에 대한 참조)에 할당한다는 점을 다시 강조하겠습니다. 따라서 두 변수는 동일한 메모리 주소를 나타냅니다. 다음 과제는 새 주소이며,이 주소는 이전 객체의 주소로 바인딩을 해결 한 다음 새 개체의 주소에 바인딩하는 것입니다. 이것은 참조로 통과하는 것 사이의 가장 중요한 차이점입니다.
또한 ECMA-262 표준이 제공하는 추상화 수준 만 고려하면 전달 된 "값"(원래 값 또는 객체 일 수 있음)을 구현하는 알고리즘에서 "값"개념 만 볼 수 있지만 위의 정의에 따르면 참조 주소도 값이기 때문에 완전히 "값으로 전달"될 수 있습니다.
그러나 오해를 피하기 위해 (외부 객체의 속성이 함수 내부에서 변경 될 수 있는가), 구현 수준의 세부 사항을 여기에서 고려해야합니다. 공유 또는 다른 말로 우리가 보는 것은 안전한 포인터로 통과 할 수 없지만 객체의 속성 값은 수정 될 수 있습니다.
용어 버전
이 정책의 용어 버전을 ECMAScript에서 정의 해 봅시다.
"Pass By Value"라고 할 수 있습니다. 여기에 언급 된 값은 특별한 경우, 즉 값은 주소 사본입니다. 이 수준에서 우리는 다음을 말할 수 있습니다. 예외를 제외한 ECMAScript의 객체는 값으로 전달됩니다. 이는 실제로 ECMAScript 추상화 수준입니다.
또는이 경우 구체적으로 "공유 통과"라고합니다. 이를 통해 전통적인 통과 가치와 참조로 통과하는 것의 차이를 볼 수 있습니다. 이 상황은 두 가지 상황으로 나눌 수 있습니다. 1 : 원래의 값은 값으로 전달됩니다. 2 : 객체는 공유로 전달됩니다.
"유형을 참조하여 기능하기"라는 문구는 ecmascript와 관련이 없으며 잘못되었습니다.
결론적으로
이 게시물이 ECMAScript에서 매크로 수준 및 구현에 대한 자세한 내용을 배우는 데 도움이되기를 바랍니다. 항상 그렇듯이 질문이 있으시면 언제든지 논의하십시오.