JavaScript는 본질적으로 캐주얼하지만 브라우저가 점점 더 많은 일을 할 수 있게 되면서 언어는 점점 더 심각해지고 있습니다. 복잡한 논리 하에서 JavaScript는 모듈화되어야 하며 모듈은 캡슐화되어야 하며 외부 호출을 위한 인터페이스만 남겨 두어야 합니다. 클로저(Closure)는 자바스크립트에서 모듈 캡슐화의 핵심이며, 많은 초보자들이 이해하기 어려워하는 포인트이기도 합니다. 처음에는 혼란스러웠습니다. 이제 나는 이 개념을 더 깊이 이해하게 되었다고 확신합니다. 이해를 돕기 위해 기사에서는 비교적 간단한 개체를 캡슐화하려고 시도합니다.
우리는 페이지에서 n 값을 유지하는 카운터 개체 티커를 유지하려고 합니다. 사용자가 작업을 수행하면 카운트를 증가시킬 수 있지만(n 값에 1을 더함) n을 감소시키거나 n을 직접 변경할 수는 없습니다. 게다가 우리는 때때로 이 값을 쿼리해야 합니다.
개방형 JSON 스타일 모듈화
문을 여는 방법은
이러한 작성 방식은 자연스럽고 효과적입니다. 개수를 늘려야 하는 경우에는 ticket.tick() 메서드를 호출합니다. 개수를 쿼리해야 하는 경우에는 ticket.n 변수에 액세스합니다. 그러나 단점도 분명합니다. 모듈 사용자는 Ticker.n-- 또는 Ticker.n=-1을 호출하는 등 자유롭게 n을 변경할 수 있습니다. 티커를 캡슐화하지 않았습니다. n 및 진드기()는 티커의 "구성원"으로 보이지만 해당 접근성은 티커와 동일하며 전역적입니다(티커가 전역 변수인 경우). 캡슐화 측면에서 이 모듈식 접근 방식은 다음 접근 방식보다 약간 더 우스꽝스럽습니다(일부 간단한 애플리케이션의 경우 이 정도만으로도 충분합니다).
tickerTick();
tick()에서 this.n에 액세스한다는 점은 주목할 가치가 있습니다. 이는 n이 Ticker의 멤버이기 때문이 아니라 Tick()을 호출하는 Ticker이기 때문입니다. 실제로, 여기에ticker.n을 작성하는 것이 더 나을 것입니다. 왜냐하면 진드기()가 호출되면 이는 티커가 아니라 다음과 같은 다른 것이기 때문입니다.
이때 실제로는 Tick()을 호출하는 것이 윈도우인데, 함수가 실행되면 window.n에 접근을 시도하여 오류가 발생하게 됩니다.
실제로 이러한 "개방형" 모듈식 접근 방식은 프로그램보다는 JSON 스타일 데이터를 구성하는 데 자주 사용됩니다. 예를 들어, 다음 JSON 객체를 Ticker 함수에 전달하여 티커가 100부터 계산을 시작하고 매번 2씩 증가하는지 확인할 수 있습니다.
스코프 체인 및 클로저
다음 코드를 보면 config를 전달하여 티커 사용자 정의를 구현했습니다.
티커가 객체에서 함수로 변경된 이유가 궁금하실 수도 있습니다. 이는 JavaScript에서는 함수에만 범위가 있고, 함수 내부 변수는 함수 본문 외부에서 액세스할 수 없기 때문입니다. Ticker() 외부에서 Ticker.n에 액세스하면 정의되지 않은 결과가 발생하지만, Tick() 내에서 n에 액세스하면 문제가 없습니다. Tick()에서 Ticker(), 전역에 이르기까지 이는 JavaScript의 "범위 체인"입니다.
하지만 여전히 문제가 있습니다. 바로 Tick()을 어떻게 호출할 것인가 하는 것입니다. Ticker()의 범위에는 Tick()도 포함됩니다. 두 가지 해결책이 있습니다:
•1) Ticker()의 반환 값으로 n을 증가시키는 메서드를 사용하는 것처럼 반환 값으로 메서드를 호출해야 합니다.
•2) 외부 범위의 변수를 설정합니다. Ticker()에서 했던 것처럼 에 getN을 설정합니다.
var ticker({nStart:100,step:2});
tick();
console.log(getN()) // ->102
참고로 이때 변수 n은 "클로저"에 있어 Ticker() 외부에서 직접 접근할 수는 없지만 두 가지 방법을 통해 관찰하거나 조작할 수 있습니다.
이 섹션의 첫 번째 코드 부분에서는 Ticker() 메서드가 실행된 후 다음에 함수가 호출될 때까지 n 및 Tick()이 소멸되지만 두 번째 코드 부분에서는 Ticker()가 이후에 삭제됩니다. 실행이 완료되면 Tick() 및 getN()이 n에 액세스하거나 변경할 수 있으므로 n은 삭제되지 않으며 브라우저는 n을 유지 관리할 책임이 있습니다. 제가 이해한 "클로저"는 함수 범위 내에 있는 변수 n이 함수 실행 후에도 유지되어야 하고 다른 메서드를 통해 액세스할 수 있는지 확인하는 데 사용되는 메커니즘입니다.
그런데 아직도 뭔가 잘못된 것 같다고요? 동일한 기능을 가진 두 개의 객체인 Ticker1과 Ticker2를 유지해야 하는 경우 어떻게 해야 합니까? 티커()는 하나뿐이라 다시 쓸 수는 없겠죠?
new 연산자와 생성자
new 연산자를 통해 함수를 호출하면 새로운 개체가 생성되고 해당 개체를 사용하여 함수가 호출됩니다. 제가 이해한 바에 따르면, 다음 코드에서 t1과 t2의 구성 과정은 동일합니다.
T1과 t2는 모두 새로 생성된 객체이고 myClass()는 생성자입니다. 마찬가지로,ticker()도 다시 작성할 수 있습니다.
varticer1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()) // ->102
var ticket2 = new TICKER({nStart:20,step:3});
ticker2.tick();
ticker2.tick();
console.log(ticker2.getN()) ; // ->26
관례에 따라 생성자는 대문자로 표시됩니다. TICKER()는 여전히 순수 객체가 아닌 함수입니다(우리가 "순수"라고 말하는 이유는 함수가 실제로 객체이고 TICKER()가 함수 객체이기 때문입니다). 클로저는 여전히 유효하며 Ticker1에 액세스할 수 없습니다. .N .
프로토타입 프로토타입 및 상속
위의 TICKER()에는 여전히 결함이 있습니다. 즉,ticer1.tick()과ticker2.tick()은 서로 독립적입니다! new 연산자를 사용하여 TICKER()를 호출할 때마다 새 객체가 생성되고 이 새 객체에 바인딩하기 위한 새 함수가 생성됩니다. 새 객체가 생성될 때마다 브라우저는 space.tick() 자체와 변수를 Tick() 내에 저장하는 것은 우리가 기대하는 것과 다릅니다. 우리는 Ticker1.tick과 Ticker2.tick이 동일한 함수 객체를 가리킬 것으로 예상합니다.
이를 위해서는 프로토타입 도입이 필요합니다.
JavaScript에서는 Object 객체를 제외한 다른 객체에는 다른 객체를 가리키는 프로토타입 속성이 있습니다. 이 "다른 객체"는 여전히 프로토타입 객체를 갖고 있으며 최종적으로 Object 객체를 가리키는 프로토타입 체인을 형성합니다. 객체에 대해 메서드를 호출할 때 객체에 지정된 메서드가 없는 것으로 확인되면 Object 객체가 나타날 때까지 프로토타입 체인에서 이 메서드를 검색합니다.
함수도 객체이므로 함수에도 프로토타입 객체가 있습니다. 함수가 선언되면(즉, 함수 객체가 정의되면) 새 객체가 생성되고 이 객체의 프로토타입 속성은 Object 객체를 가리키며 이 객체의 constructor 속성은 함수 객체를 가리킵니다.
생성자를 통해 생성된 새 객체의 프로토타입은 생성자의 프로토타입 객체를 가리킵니다. 따라서 생성자의 프로토타입 객체에 함수를 추가할 수 있으며 이러한 함수는 Ticker1이나 Ticker2에 의존하지 않고 TICKER에 의존합니다.
다음을 수행할 수 있습니다.
이것은 유효하지 않은 구현입니다. 프로토타입 객체의 메서드는 클로저의 내용, 즉 변수 n에 접근할 수 없기 때문입니다. TICK() 메서드가 실행된 후에는 n에 더 이상 액세스할 수 없으며 브라우저는 n을 삭제합니다. 클로저의 콘텐츠에 액세스하려면 객체에 클로저의 콘텐츠에 액세스할 수 있는 몇 가지 간결한 인스턴스 종속 메서드가 있어야 하며 그런 다음 프로토타입에 복잡한 공용 메서드를 정의하여 논리를 구현해야 합니다. 실제로 예제의 Tick() 메서드는 충분히 간결하므로 TICKER에 다시 넣어 보겠습니다. 다음으로 호출자가 Tick()이 호출되는 횟수를 지정할 수 있도록 하는 더 복잡한 메서드인 TickTimes()를 구현합니다.
이 TICKER는 훌륭합니다. n을 캡슐화하고 객체 외부에서 직접 변경할 수 없으며 복잡한 함수인 TickTimes()가 프로토타입에 정의되어 있습니다. 이 함수는 인스턴스의 작은 함수를 호출하여 객체의 데이터에 대해 작동합니다.
따라서 개체의 캡슐화를 유지하기 위해 제가 제안하는 것은 데이터 작업을 생성자에서 인스턴스 종속(" "비공개"라고도 함)으로 정의된 가능한 가장 작은 단위 함수로 분리하는 것입니다. 프로토타입(예: "공개")에 복잡한 논리를 구현합니다.
마지막으로 상속에 대해 이야기해보겠습니다. 사실, 프로토타입에 함수를 정의할 때 우리는 이미 상속을 사용하고 있습니다! JavaScript의 상속은 C보다 더...음...간단하거나 조잡합니다. C에서는 동물을 표현하기 위해 동물 클래스를 정의하고, 새를 표현하기 위해 동물 클래스를 상속받기 위해 새 클래스를 정의할 수 있지만, 제가 논의하고 싶은 것은 그러한 상속이 아닙니다(이러한 상속은 JavaScript에서도 구현될 수 있지만). ); C에서 상속에 대한 논의는 동물 클래스를 정의한 다음 myAnimal 객체를 인스턴스화하는 것입니다. 예, C에서는 인스턴스화이지만 JavaScript에서는 상속으로 처리됩니다.
JavaScript는 클래스를 지원하지 않습니다. 브라우저는 현재 사용 가능한 객체에만 관심을 갖고 이러한 객체가 어떤 클래스인지, 어떤 구조를 가져야 하는지 걱정하지 않습니다. 이 예에서 TICKER()는 함수 개체입니다. 그러나 현재 new 연산자를 통해 TiCKER1과 Ticker2라는 두 개체가 있으므로 값을 할당하고(TICKER=1) 삭제할 수 있습니다. 호출되면 TICKER()가 생성자 역할을 하고 TICKER.prototype 객체가 클래스 역할을 합니다.
위 내용은 제가 알고 있는 자바스크립트 모듈화 방법입니다. 혹시 초보자이시더라도 도움이 되셨으면 좋겠습니다. 잘못된 부분이 있으면 지적해주세요.
저자: Master Yiyezhai
출처: www.cnblogs.com/yiyezhai