반응형
1. 값인 함수
- 자바스크립트의 함수는 값으로써 변수에 할당될 수 있고 객체프로퍼티,
배열 요소에 저장될 수 있으며, 다른 함수에 인자로 전달이 될 수 있음
- 함수는 프로퍼티를 가질 수 있음
// 값인 함수 --------------------------------------------------
// 함수 정의
function add(x,y) { return x + y;}
function subtract(x, y) { return x - y; }
function multiply(x,y) { return x * y; }
function divide(x, y) { return x / y; }
// 앞에서 정의한 함수 중 하나를 인자로 받아
// 그 함수에 다른 두 인자를 전달해 호출하는 함수
function operate(operator, operand1, operand2) {
return operator(operand1, operand2);
}
// 이 함수를 사용해 (2+3) + (4*5) 같은 값을 계산할 수 있음
let i = operate(add, operate(add, 2, 3), operate(multiply, 4, 5));
// 객체 리터럴로 만든다.
const operators = {
add: (x,y) => x+y,
subtract: (x,y) => x-y,
multiply: (x,y) => x*y,
divide: (x,y) => x/y,
pow: Math.pow //미리 정의한 함수 사용 가능
}
// 매개변수 연산자 이름을 통해 객체에서 연산자를 찾고
// 매개변수 피연산자를 찾은 연산자를 통해 계산
function operate2(operation, operand1, operand2) {
if (typeof operators[operation] == "function") {
return operators[operation](operand1, operand2);
}
else throw "unknown operator";
}
operate2("add","hello", operate2("add", " ", "world")) // => "hello world"
operate2("pow", 10, 2) // => 100
// 함수 프로퍼티 직접 정의 -----------------------------------------
// 객체 프로퍼티
// 함수 객체의 counter 프로퍼티를 초기화
uniqueInteger.counter = 0;
// 이 함수는 호출할 때마다 다른 정수를 반환
// 자신의 프로퍼티를 사용해 어떤 값을 반환할지 판단
function uniqueInteger() {
return uniqueInteger.counter++; // counter 프로퍼티를 반환하고 증가
}
uniqueInteger() // => 0
uniqueInteger() // => 1
// 배열 프로퍼티
// 팩토리얼을 계산하고 그 결과를 함수 잔체의 프로퍼티를 캐시함
function factorial(n) {
if(Number.isInteger(n) && n > 0) { // 양의 정수만 사용
if (!(n in factorial) { // 캐시된 결과가 없다면
factorial[n] = n * factorial(n-1); // 계산하고 캐시에 저장
}
return factorial[n]; // 캐시된 결과를 반환
} else {
return NaN;
}
}
factorial[1] = 1; // 캐시 초기화
factorial(6) // => 720
factorial[5] // => 120; 이 값은 이미 캐시에 존재
}
}
2. 네임스페스인 함수
- 코드의 정의한 변수가 다른 프로그램과 충동할 수 있음으로 특정 프로그램
에만 사용할 수 있도록 변수를 함수에 정의하는 방법이 있음
function chunkNamespace() {
// 코드가 여기 존재함, 코드에 정의한 변수는 모두함수의 로컬 변수이므로
// 전역 네임스페이스를 어지럽히는 일은 없음
}
chunkNamespace(); // 단, 이 함수 호출은 잊지 말아야함
// IIFE(즉시 호출하는 함수 표현식)
(function() { // chunkNamespace() 함수를 익명 표현식으로 변경
// 코드가 존재함
}()); // 함수 리터럴을 종료하고 즉시 호출
3. 클로저
- 자바스크립트는 어휘적 스코프(lexial scope)를 사용
- 어휘적 스코프 : 호출 시점의 스코프라 아니라 자신이 정의된 시점의 변수 스코프를
사용하여 실행
- 함수가 중첩되어 있는 상황에서 외부 함수가가 내부 함수보다 오래 유지되면, 외부 함수 밖에서
내부함수가 호출되더라도 외부 함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(closure)라고 함
// 클로저 예제 --------------------------------------------------
// 내부 함수를 통해 외부 함수의 로컬 변수값을 반환
let scope = "global scope"; // 전역 변수
function checkscope() {
let scope = "local scope"; // 로컬 변수
function f() {return scope; } 이 스코프에 있는 값 반환
}
console.log(checkscope()()) // => "local scope"
// 비공개 상태 사용(클로저 활용) --------------------------------
let uniqueInteger = (function() {
let counter = 0;
return function() {return counter++;};
}())
uniqueInteger() // => 0
uniqueInteger() // => 1
// uniqueInteger는 내부함수가 할당되고
// uniqueInteger는 실행할때 마다 외부함수의
// counter 지역 변수를 증가시킨다
// 여러개의 함수를 객체에 묶어 반환 --------------------------------
function counter() {
let n = 0;
return {
count: function() {return n++; }
reset: function() {n = 0; }
};
}
let c = counter(), d = counter(); // 카운터 두개 생성
c.count() // => 0
d.count() // => 0
c.reset() // reset, count는 n 상태 공유
c.count() // => 0 : c는 리셋
d.count() // => 1 : d는 리셋되지 않음
// 선언된 두개 c, d의 카운터는 서로에게 영향을 주지 않음
// 클로저를 활용한 세터, 게터--------------------------------------
function counter(n) {
return {
// 프로퍼티 게터 메서드를 비공개 카운터 변수를 반환하고 증가
get count() { return n++; },
// 프로퍼티 세터는 n 값의 감소를 허용하지 않음
set count(m) {
if (m > n) n = m;
else throw Error("카운트는 더 큰 값으로만 바꿀 수 있음")
}
};
}
let c = counter(1000);
c.count // => 1000
c.count // => 1001
c.count = 2000
c.count // => 2000
c.count = 2000; // Error: 카운트는 더 큰 값만 바꿀 수 있음
// 클로저를 사용한 비공개 프로퍼티 접근자 메서드--------------------
// 이 함수는 지정된 이름의 프로퍼티에 대한 프로퍼티 접근자 메서들 객체 o에 추가
// 메서드 이름은 get<name>과 set<name>으로 지정
// 판별 함수가 제공됐다면 세터 메서드는 인자를 저장하기 전에 판별 함수를 사용해
// 유효성을 테스트한다. 판별 함수가 false 반환하면 세터 메서드가 예외를 일으키다.
// 이 함수의 독특한 점은 게터와 세터 메서드가 조작하는 프로퍼티 값이
// 객체 o에 저장되지 않는다는 점이다. 값은 이 함수의 로컬 변수에만 저장된다.
// 게터와 세터 메서드는 함수에 로컬로 정의됐다면 로컬 변수에 접근할 수 있다.
// 따라서 값은 두 접근자 메서드에서만 사용할 수 있으며, 세터 메서드를 통하지 않고서는
// 값을 수정하거나 저장 할 수 없음
function addPrivateProperty(o, name, predicate) {
let value; //프로퍼티 값
// 게터 메서드는 단순히 그 값을 반환
o[`get${name}`] = function() {return value;};
// 세터 메서드는 판별 함수의 판단에 따라 값을 저정하거나 예외를 발생시킴
o[`set${name}`] = function(v) {
if (predicate && !predicate(v)) {
throw new TypeError(`set${name}: invalid value ${v}`);
} else {
value = v;
}
};
}
let o = {};
addPrivateProperty(o, "Name", x => typeof x === "string");
o.setName("Frank") // 프로퍼티의 값을 지정
o.getName() // => "Frank"
o.setName(0); // TypeError: 올바르지 않은 타입을 사용
// 클로저 사용시 주의할 점 ------------------------------------------
function constFuncs() {
let funcs = [];
for(var i=0; i<10; i++) {
funcs[i] = () => i;
}
return funcs
}
let funcs = constFuncs();
funcs[5]() // => 10;
// i의 값을 클로저 함수가 공유하고 있기 때문에
// i의 마지막값이 나옴(var로 선언하면 함수 전체에 i값이 존재하게됨)
// for문의 i를 var 선언하지 않고 const, let 선언시 해결
// const, let은 블록스코프여서 for문 안에서만 유효함
반응형