다루는 내용
- 함수 표현식의 특징
- 함수와 재귀
- 클로저를 이용한 고유(프라이빗) 변수
1. 함수 표현식의 특징
함수를 정의하는 방법은 함수 선언과 함수 표현식 2가지가 있음.
함수 선언에서 뚜렷한 특징은 호이스팅(hoisting)이다.
sayHi(); function sayHi() { console.log("hi"); } // 함수 선언부를 다른 코드보다 먼저 읽고 실행함
함수 표현식은 일반적인 변수 할당과 거의 비슷하며 함수 이름이 없어 익명 함수로 간주함
sayHi(); // error var sayHi = function() { console.log("hi"); } // 다른 표현식과 마찬가지로 호출 하기 전에 할당해야함
함수 표현식은 다른 함수에서 사용할 수 있도록 함수를 반환하는 형태도 가능
function compareFunction(propertyName) { return function(object1, object2) { var val1 = object1[propertyName]; var val2 = object2[propertyName]; if(val1 < val2) { return -1; } else if(val1 > val2) { return 1; } else { return 0; } }; }
2. 함수와 재귀
2-1 재귀
함수가 자기 자신을 호출하는 형태
function factorial(num) { if(num <= 1) { return 1; } else { return num * factorial(num-1); } } var anotherFactorial = factorial; factorial = null; console.log(anotherFactorial(4)); // error // factorial 은 null 인데 anotherFactorial 이 factorial()을 실행하려 하기 때문
arguments.callee 를 호출하면 되지만, 이는 스트릭트 모드에서 접근할 수 없기에,
이름 붙은 함수 표현식
을 사용var factorial = (function f(num) { if (num <= 1) { return 1; } else { return num * f(num-1); } });
2-2 클로저
다른 함수의 스코프에 있는 변수에 접근 가능한 함수
function compareFunction(propertyName) { return function(object1, object2) { var val1 = object1[propertyName]; var val2 = object2[propertyName]; if(val1 < val2) { return -1; } else if(val1 > val2) { return 1; } else { return 0; } }; } var compare = compareFunction("name"); var result = compare({name : "snack"}, {name : "gray"});
- 외부 함수가 실행을 마치고 익명 함수를 반환하면 익명 함수의 스코프 체인은 외부 함수의 활성화 객체와 전역 변수 객체를 포함하도록 초기화되므로, 익명 함수는 외부 함수의 변수 전체에 접근 가능
- 외부 함수가 실행을 마치면 실행 컨텍스트의 스코프 체인은 파괴되지만, 활성화 객체는 익명 함수가 파괴될 때까지 메모리에 남음
- compare = null 을 통해 함수에 대한 참조를 사라지게 하여 GC 가 메모리 회수가 가능하게 함
- 클로저는 외부 스코프를 보관해야 하므로 메모리를 많이 잡아먹는다. 그러므로 남용은 지양
- 클로저는 항상 외부 함수의 변수에 마지막으로 저장된 값만 알 수 있음 (전체 변수 객체에 대한 참조를 저장하기 때문)
function createdFunction() { var result = new Array(); for(var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i); } return result; }
- 변수 i 를 익명함수에 매개변수로 넘김
- 함수 매개변수는 값 형태로 전달되므로 i의 현재 값을 매개변수 num 에 복사
- 익명함수는 num 을 간직한 클로저를 생성하여 반환
- this 객체는 런타임에서 함수가 실행 중인 컨텍스트에 묶이는데, 익명함수는 특정 객체에 묶여 있지 않으므로 mode == strict mode ? undefined : window
- 모든 함수는 호출되는 순간 자동으로 this와 arguments 두 특별한 변수를 갖게되는데, 내부 함수는 결코 외부 함수의 this 와 arguments 에 직접적으로 접근이 불가능, 그러므로 다음과 같이 해서 접근 가능
var name = "the window"; var object = { name : "my object", getNameFunc : function() { var that = this; return function() { return that.name; }; } }; console.log(object.getNameFunc()()); // my object
- 블록 레벨 스코프라는 개념이 없기 때문에 익명 함수를 괄호로 감싸서 흉내낼 수 있음 (
고유 스코프
라고 부르기도 함)
function outputFunc(count) { (function () { for(var i=0; i < countl i++) { console.log(i); } })(); console.log(i); // error }
- 전역 스코프에 추가되는 변수나 함수의 수를 제한하는 용도로 자주 사용 (대규모 어플리케이션에선 전역 스코프에 변수나 함수를 추가하지 않는 편이 좋음)
- 익명 함수에 대한 참조가 존재하지 않아 클로저의 메모리 문제도 덜함
3. 클로저를 이용한 고유(프라이빗) 변수
자바스크립트에는 private member 이란 개념이 없지만,
고유 변수
라는 개념은 존재함수 내부에서 정의한 변수는 함수 밖에서 접근할 수 없으므로 모두
고유 변수
로 간주 (함수 매개변수, 지역 변수, 내부 함수 등)다음과 같이 고유 및 특권 맴버를 정의해서 데이터를 직접적으로 수정할 수 없게 보호 가능
function Person(name) { this.getName = function() { return name; }; this.setName = function(value) { name = value; }; } var person = new Person("snack"); console.log(person.getName()); // snack person.setName("gray"); console.log(person.getName()); // gray
생성자 패턴에는 인스턴스가 매번 생성되므로, 이를 방지하기 위해 정적 고유 변수를 사용
(function() { // 고유 변수와 함수 var name = ""; function privateFunction() { return false; } //생성자 Person = function(value) { name = value }; // 공용 메소드와 특권 메소드 Person.prototype.getName = function() { return name; }; Person.prototype.setName = function(value) { name = value; }; })(); var person1 = new Person("snack"); console.log(person1.getName()); // snack person1.setName("gray"); console.log(person1.getName()); // gray var person2 = new Person("john"); console.log(person1.getName()); // john console.log(person2.getName()); // john
- 각 인스턴스가 독립 변수를 가질 수는 없지만, 프로토타입을 통해 코드 재사용성은 좋아짐
싱글톤 같은 일을 하는 모듈 패턴이 있음
var application = function() { //고유 변수와 함수 var components = new Array(); //초기화 components.push(new BaseComponent()); //공용 인터페이스 return { getComponentCount : function() { return components.length; }, registerComponent : function(component) { if(typeof component == "object") { components.push(component); } } }; }();
- 애플리케이션 레벨의 정보를 관리할 싱글톤을 두는 경우가 많음
- 하나의 객체를 반드시 생성하고 몇 가지 데이터를 가지며 또한 고유 데이터에 접근 가능한 공용 메소드를 외부에 노출하도록 초기화해야 할 때 유용
싱글톤 객체가 특정 타입의 인스턴스지만 프로퍼티나 메서드를 추가하여 확장해야 할 때
모듈 확장 패턴
을 사용var application = function() { //고유 변수와 함수 var componens = new Array(); //초기화 components.push(new BaseComponent()); //애플리케이션 인스턴스 생성 var app = new BaseComponent(); //공용 인터페이스 app.getComponentCount = function() { return components.length; } app.registerComponent = function(component) { if(typeof component == "object") { components.push(component); } }; return app; }();
- app 은 application 객체의 로컬 버전
'개발 관련 > javascript' 카테고리의 다른 글
10. DOM (0) | 2022.07.13 |
---|---|
8. 브라우저 객체 모델 (0) | 2022.07.13 |
6. 객체 지향 프로그래밍 (0) | 2022.07.13 |
5. 참조 타입 (0) | 2022.07.13 |
4. 변수, 스코프, 메모리 (0) | 2022.07.13 |