정보실

웹학교

정보실

javascript 제 4 장 JavaScript의 클로저 및 모듈

본문

글로벌 광기(Global madness) 


전역 변수는 소프트웨어 공학의 전염병입니다. 글로벌은 일부 상황에서 유용하지만 모든 비용을 피하는 방법을 배웠습니다. 예를 들어 브라우저에서 JavaScript로 작업 할 때 전역 창 개체에 액세스 할 수 있습니다. 창에는 다음과 같은 유용한 방법이 많이 있습니다.


window.alert('Hello world'); // Shows an alert
window.setTimeout(callback, 3000); // Delay execution
window.fetch(someUrl); // make XHR requests
window.open(); // Opens a new tab


이러한 방법은 다음과 같이 전역적으로 제공됩니다.


https://github.com/valentinogagliardi/Little-JavaScript-Book/blob/v1.0.0/manuscript/chapter4.md 


alert('Hello world'); // Shows an alert
setTimeout(callback, 3000); // Delay execution
fetch(someUrl); // make XHR requests
open(); // Opens a new tab


편리합니다. Redux는 "좋은"전역의 또 다른 예입니다. 전체 응용 프로그램의 상태는 Redux를 통해 전체 응용 프로그램에서 액세스 할 수 있는 단일 JavaScript 객체에 저장됩니다. 그러나 50 명의 개발자가 기능을 제공 해야 하는 팀에서 JavaScript 코드를 작성할 때 ... 다음과 같은 코드에 어떻게 대처합니까?

var arr = [];

function addToArr(element) {
  arr.push(element);
  return element + " added!";
}


다른 파일에 arr이라는 새로운 전역 배열을 만드는 동료의 가능성은 무엇입니까? 당신이 나에게 묻는다면 매우 높습니다! JavaScript의 전역이 실제로 나쁜 또 다른 이유는 엔진이 우리를 위해 전역 변수를 생성하기에 충분히 친절하기 때문입니다. 의미하는 바는 변수 이름 앞에 var를 넣는 것을 잊어 버린 경우와 같습니다.


name = "Valentino";


엔진이 전역 변수를 생성합니다! Shudder ... "의도하지 않은"변수가 함수 내에 생성 될 때 최악의 경우 :


function doStuff() {
  name = "Valentino";
}

doStuff();

console.log(name); // "Valentino"


무고한 기능으로 인해 지구 환경이 오염되었습니다. 운 좋게도 이 모드를 "엄격 모드"로 중화하는 방법이 있습니다. 모든 JavaScript 파일 맨 위에 "엄격한 사용"이라는 문장은 바보 같은 실수를 피하기에 충분합니다.


"use strict";

function doStuff() {
  name = "Valentino";
}

doStuff();

console.log(name); // ReferenceError: name is not defined


항상 엄격하게 사용하십시오! 이와 같은 오류는 요즘 해결 된 문제입니다. 개발자들은 점점 더 자기 훈련을 받고 있지만 (구글 희망) 예전에는 전역 변수와 절차 코드가 JavaScript의 표준이었습니다. 그래서 우리는 "전역"문제를 해결할 수 있는 방법을 찾아야 했으며 운 좋게도 JavaScript에는 항상 이를 위한 메커니즘이 내장되어 있었습니다.


클로저 이해하기 


그렇다면 우리는 어떻게 글로벌 변수가 눈에 띄지 않도록 보호합니까? 함수 내에서 arr을 이동하여 순진한 솔루션으로 시작합시다.


function addToArr(element) {
  var arr = [];
  arr.push(element);
  return element + " added to " + arr;
}


합리적으로 보이지만 결과는 내가 기대 한 것이 아닙니다.


var firstPass = addToArr("a");
var secondPass = addToArr("b");
console.log(firstPass); // a added to a
console.log(secondPass); // a added to a


arr은 모든 함수 호출에서 계속 재설정 합니다. 이제는 지역 변수이지만 첫 번째 예에서는 전역 변수로 선언되었습니다. 전역 변수가 "실시간"이며 JavaScript 엔진에 의해 파괴되지 않습니다. 반면에 지역 변수는 가비지 수집이라는 프로세스의 일부로 엔진에 의해 지워집니다 (나는 당신에게 뻔뻔 스럽지만 여기에 주제에 대한 흥미로운 기사가 ​​있습니다 : V8 : 가비지 수집 둘러보기). 지역 변수가 파괴되는 것을 막을 방법이 없는 것 같습니다. 아니면 있습니까? closure가 도움이 될까요? 그러나 어쨌든 closure는 무엇입니까?


JavaScript 함수에 다른 함수가 포함될 수 있다는 것은 놀라운 일이 아닙니다. 다음은 완벽하게 유효한 코드입니다.


function addToArr(element) {
  var arr = [];

  function push() {
    arr.push(element);
  }

  return element + " added to " + arr;
}


코드는 아무것도 하지 않습니다 ... 10 년 전 비슷한 문제를 해결하려고 했습니다. 간단한 자체 포함 된 함수를 동료에게 배포하고 싶었습니다. 전역 변수가 많은 JavaScript 파일을 처리 할 수 ​​없습니다. 함수 내부에 로컬 상태를 유지하면서 변수를 보호하는 방법을 찾아야 했습니다. 필사적으로 절망할 때까지 말 그대로 모든 것을 시도해 보았고 다음과 같이 연구했습니다.


function addToArr(element) {
  var arr = [];

  return function push() {
    arr.push(element);
    console.log(arr);
  };

  //return element + " added to " + arr;
}


외부 함수는 단순한 컨테이너가 되어 다른 함수를 반환합니다. 두 번째 return 문은 해당 코드에 도달하지 않으므로 주석 처리됩니다. 이 시점에서 함수 호출 결과를 다음과 같이 변수 안에 저장할 수 있습니다.


var result = addToArr();


이제 result는 실행 가능한 JavaScript 함수가 됩니다.


var result = addToArr();
result("a");
result("b");


매개 변수 "element"를 외부에서 내부 함수로 이동하여 수정해야 합니다.


function addToArr() {
  var arr = [];

  return function push(element) {
    arr.push(element);
    console.log(arr);
  };

  //return element + " added to " + arr;
}


그리고 그것이 마법이 일어나는 곳입니다. 완전한 코드는 다음과 같습니다.


function addToArr() {
  var arr = [];

  return function push(element) {
    arr.push(element);
    console.log(arr);
  };

  //return element + " added to " + arr;
}

var result = addToArr();
result("a"); // [ 'a' ]
result("b"); // [ 'a', 'b' ]


빙고! 이 마법은 무엇입니까? 환경을 기억할 수 있는 함수 인 JavaScript 클로저라고 합니다. 이렇게 하려면 내부 함수는 둘러싸는 (외부) 함수의 반환 값이어야 합니다. 이 프로그래밍 기술은 괴상한 원에서 팩토리 함수라고도 합니다. 코드를 약간 조정하면 변수의 이름을 더 잘 지정할 수 있으며 원하는 경우 내부 함수는 익명 일 수 있습니다.


function addToArr() {
  var arr = [];

  return function(element) {
    arr.push(element);
    return element + " added to " + arr;
  };
}

var closure = addToArr();
console.log(closure("a")); // a added to a
console.log(closure("b")); // b added to a,b


"closure"가 내부 함수라는 것을 분명히 해야 합니다. 그러나 한 가지 문제를 해결해야 합니다. 왜 우리는 이것을 하는가? JavaScript 클로저의 실제 목적은 무엇입니까?


closures의 필요성 


단순한 "학술적"지식 외에도 JavaScript 클로저는 여러 가지 이유로 유용합니다. 

그들은 다음을 도와줍니다.

  • 그렇지 않으면 전역 변수에 개인 정보를 제공
  • 함수 호출 사이에 변수 (상태) 유지

JavaScript에서 클로저를 위한 가장 흥미로운 응용 프로그램 중 하나는 모듈 패턴입니다. ES 모듈을 도입한 ECMAScript 2015 이전에는 JavaScript 코드를 모듈화 할 수 있는 방법이 없었으며, 함수 내부에 랩핑하는 것 외에 변수 및 메소드에 "개인 정보"를 제공 할 수 없었습니다. 즉시 호출 된 함수 표현식과 쌍을 이루는 클로저는 여전히 JavaScript 코드를 구성하는 가장 자연스러운 솔루션입니다. 가장 간단한 형태의 JavaScript 모듈은 IIFE (immediately invoked function expression,즉시 호출 된 함수 표현식)의 결과를 캡처하는 변수입니다.


var Person = (function() {
  // do stuff
})();


모듈 내부에는 "비공개"변수와 메소드가 있습니다 :


var Person = (function() {
  var person = {
    name: "",
    age: 0
  };

  function setName(personName) {
    person.name = personName;
  }

  function setAge(personAge) {
    person.age = personAge;
  }
})();


외부에서는 person.name 또는 person.age에 액세스하거나 setName 또는 setAge를 호출 할 수 없습니다. 모듈 내부의 모든 것은 "개인"입니다. 메소드를 공개적으로 공개하려면 개인 메소드에 대한 참조를 포함하는 오브젝트를 리턴 할 수 있습니다.


var Person = (function() {
  var person = {
    name: "",
    age: 0
  };

  function setName(personName) {
    person.name = personName;
  }

  function setAge(personAge) {
    person.age = personAge;
  }

  return {
    setName: setName,
    setAge: setAge
  };
})();


person 객체는 여전히 숨겨져 있지만 새 사람을 반환하는 방법을 쉽게 추가 할 수 있습니다.


var Person = (function() {
  var person = {
    name: "",
    age: 0
  };

  function setName(personName) {
    person.name = personName;
  }

  function setAge(personAge) {
    person.age = personAge;
  }

  function getPerson() {
    return person.name + " " + person.age;
  }

  return {
    setName: setName,
    setAge: setAge,
    getPerson: getPerson
  };
})();

Person.setName("Tom");
Person.setAge(44);
var person = Person.getPerson();
console.log(person); // Tom 44


이런 식으로 사람은 여전히 ​​외부에서 직접 접근 할 수 없습니다 :


console.log(Person.person); // undefined


JavaScript 패턴을 구성하는 유일한 방법은 모듈 패턴이 아닙니다. 객체를 사용하면 동일한 결과를 얻을 수 있습니다.


var Person = {
  name: "",
  age: 0,
  setName: function(personName) {
    this.name = personName;
  }
  // other methods here
};


그러나 이제 JavaScript 객체에서 예상 한대로 내부 속성이 유출됩니다.


var Person = {
  name: "",
  age: 0,
  setName: function(personName) {
    this.name = personName;
  }
  // other methods here
};

Person.setName("Tom");

console.log(Person.name); // Tom


이것이 모듈의 주요 판매 지점 중 하나입니다. 또 다른 이점은 모듈이 자바 스크립트 코드를 관용적이고 읽기 쉬운 방식으로 구성하는 데 도움이 된다는 것입니다. 다시 말해, 거의 모든 JavaScript 개발자는 다음 코드를 보고 그 목적을 추측 할 수 있습니다.


"use strict";

var Person = (function() {
  var person = {
    name: "",
    age: 0
  };

  function setName(personName) {
    person.name = personName;
  }

  function setAge(personAge) {
    person.age = personAge;
  }

  function getPerson() {
    return person.name + " " + person.age;
  }

  return {
    setName: setName,
    setAge: setAge,
    getPerson: getPerson
  };
})();


결론 


전역 변수는 나쁘므로 가능하면 피해야 합니다. 때로는 유용하지만 JavaScript에서는 엔진이 얇은 공기에서 전역 변수를 생성하기 위해 자유를 취하기 때문에 특히 주의해야 합니다. 글로벌 변수를 길들이기 위해 수년 동안 많은 패턴이 나타 났으며, 그 중 하나는 모듈 패턴입니다. 모듈 패턴은 JavaScript의 고유 기능인 클로저를 기반으로 합니다. JavaScript의 클로저는 후속 함수 호출 사이에서도 변수의 환경을 "기억"할 수 있는 함수입니다. "공장 함수"라고도 하는 패턴인 다른 함수에서 함수를 반환하면 클로저가 생성됩니다.



페이지 정보

조회 49회 ]  작성일19-10-17 19:58

웹학교