함수 객체에 내장된 프로퍼티
- name : 함수 이름 리턴.
- length : 함수 매개변수의 개수 반환
프로퍼티는 변수가 아님.
커스텀 프로퍼티.
=> 함수에 자체적으로 만든 프로퍼티를 추가할 수 있음.
js 라이브러리인 jQuery는 $라는 주요 함수로 이뤄져 있다. 여기에 헬퍼 함수를 붙여 사용.
기명 함수 표현식(Named Function Expression, NFE)
=> 이름이 있는 함수 표현식.
1) 이름(func)을 사용해 함수 표현식 내부에서 자기 자신을 참조할 수 있다.
2) 함수 외부에선 그 이름 사용 불가.
let sayHi = function func(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
func("Guest"); // func를 사용해서 자신을 호출합니다.
}
};
sayHi(); // Hello, Guest
// 하지만 아래와 같이 func를 호출하는 건 불가능합니다.
func(); // Error, func is not defined (기명 함수 표현식 밖에서는 그 이름에 접근할 수 없습니다.)
func대신 sayHi()를 함수 내에서 사용할 경우, sayHi=null; 으로 null값 저장 시 에러가 나옴. (함수가 자신 sayHi()를 외부 렉시컬 환경에서 가지고 오기 때문에, 가지고 올 당시 이미 null값을 부여받은 것을 인지하여 에러 반환)
대신 내부 이름인 func를 사용하면 내부에서 자신을 호출하므로 제대로 동작하게 된다.
함수 표현식, 함수 선언문 외에 함수를 만들 수 있는, 잘 사용되지는 않는 방법.
let func = new Function ([arg1, arg2, ...argN], functionBody);
기존의 함수 선언식
function 함수명() {
구현 로직
}
함수 표현식
var 함수명 = function () {
구현 로직
};
new Function을 사용해 함수를 만드는 방법은 런타임에 받은 문자열을 사용해 함수를 만들 수 있다는 장점이 있다.
let str = ... 서버에서 동적으로 전달받은 문자열(코드 형태) ...
let func = new Function(str);
func();
문자열을 코드로 인식해 실행한다는 점을 주목할 것.
때문에, 서버에서 코드를 받거나, 템플릿을 사용해 함수를 동적으로 컴파일해야 하는 경우 등등, 특별한 경우에 new Function 사용.
함수가 생성될 때 같이 정해지는 [[Environmentn]]를 통해 함수는 자신이 태어난 곳을 기억한다.
new Function을 통해 함수를 만들경우 전역 렉시컬 환경을 참조하게 됨. 전역이 아닌 외부 변수 참조 불가.
function getFunc() {
let value = "test";
let func = new Function('alert(value)');
return func;
}
getFunc()(); // ReferenceError: value is not defined
호출 스케줄링(scheduling a call) : 일정 시간이 지난 후에 원하는 함수를 예약 실행(호출)할 수 있게 하는 것
1) setTimeout : 일정 시간 지난 후 함수 실행 (1회)
2) setInterval : 일정 시간 간격을 두고 함수 실행
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
func에 함수를 넣는것은 추천되지 않음. 가능하면 익명 화살표 함수를 대신 사용할 것.
//---문자열
setTimeout("alert('안녕하세요.')", 1000);
//---화살표함수
setTimeout(() => alert('안녕하세요.'), 1000);
*** 함수를 넘길 때는 함수 뒤에 괄호 없이 넘기기. 함수이름() 형태로 넘기면, 함수 실행 결과가 전달됨.
clearTimeout으로 스케줄링 취소하기
let timerId = setTimeout(...);
clearTimeout(timerId);
setInterval
let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)
=> clearInterval(timerID)로 함수 호출 중단.
중첩 setTimeout
/** setInterval을 이용하지 않고 아래와 같이 중첩 setTimeout을 사용함
let timerId = setInterval(() => alert('째깍'), 2000);
*/
let timerId = setTimeout(function tick() {
alert('째깍');
timerId = setTimeout(tick, 2000); // (*)
}, 2000);
=> setInterval 사용하는 것보다 유연. 다음 호출을 보다 유연하게 조정할 수 있기 때문. ex) 서버 과부하 상태일 경우 요청 간격을 2배씩 증가해 요청 보냄.
SetInterval을 사용하면 함수 실행 시 소모되는 시간도 지연 간격에 포함되어 실제 명시한 시간 간격보다 짧아진다.
setTimeout을 사용하면 명시한 지연 시간이 보장됨 ( 함수 실행 종료 이후에 다음 함수 호출에 대한 계획이 세워짐.)
*** setInterval, setTimeout이 더 이상 참조되지 않더라도 GC(Garbage Collection)의 대상이 되지 않으므로 clearInterval, clearTimeout을 사용하여 취소해 줘야 메모리 공간 낭비를 막을 수 있다.
대기 시간이 0인 setTimeout
: 대기 시간을 0으로 설정 시 가능한 한 빨리 해당 기능을 실행. 즉, 현재 스크립트 실행 종료 직후에 해당 함수 실행가능.
*** 다섯 번째 중첩 타이머 이후엔 대기 시간이 자동으로 4밀리 초 이상 늘어남 (브라우저상에서 처리됨)
CPU를 많이 잡아먹지만 결과는 안정적인 함수가 있다면, 결과를 캐싱(저장)해 재 연산에 걸리는 시간을 줄이고 싶게 될 것.
래퍼 함수를 만들어 캐싱 기능을 추가한 예)
function slow(x) {
// CPU 집약적인 작업이 여기에 올 수 있습니다.
alert(`slow(${x})을/를 호출함`);
return x;
}
function cachingDecorator(func) {
let cache = new Map();
return function(x) {
if (cache.has(x)) { // cache에 해당 키가 있으면
return cache.get(x); // 대응하는 값을 cache에서 읽어옵니다.
}
let result = func(x); // 그렇지 않은 경우엔 func를 호출하고,
cache.set(x, result); // 그 결과를 캐싱(저장)합니다.
return result;
};
}
slow = cachingDecorator(slow);
alert( slow(1) ); // slow(1)이 저장되었습니다.
alert( "다시 호출: " + slow(1) ); // 동일한 결과
alert( slow(2) ); // slow(2)가 저장되었습니다.
alert( "다시 호출: " + slow(2) ); // 윗줄과 동일한 결과
*** 데코레이터(Decorator) : 위의 cachingDecorator와 같이 인수로 받은 함수의 행동을 변경시켜주는 함수.
모든 함수를 대상으로 데코레이터 함수를 사용할 수 있기 때문에 유용하게 사용됨.
=> 함수를 직접 수정하는 것에 비해 재사용이 가능하고, 함수 자체의 복잡성이 높아지지 않으며, 여러 개의 데코레이터를 조합해서 사용 가능하다는 장점이 있다.
func.call 사용해 컨텍스트 지정하기.
캐싱 데코레이터는 객체 메서드에 사용하기엔 적합하지 않다.
func.call(context,... args)
func.call(context, arg1, arg2, ...)
func.call으로 this값을 명시적으로 정해주면 함수를 호출할 수 있음!
func의 첫 번째 인수를 this로 제공하고, 나머지를 인수로 준다.
여러 인수 전달하기
let worker = {
slow(min, max) {
alert(`slow(${min},${max})을/를 호출함`);
return min + max;
}
};
function cachingDecorator(func, hash) {
let cache = new Map();
return function() {
let key = hash(arguments); // (*)
if (cache.has(key)) {
return cache.get(key);
}
let result = func.call(this, ...arguments); // (**)
cache.set(key, result);
return result;
};
}
function hash(args) {
return args[0] + ',' + args[1];
}
worker.slow = cachingDecorator(worker.slow, hash);
alert( worker.slow(3, 5) ); // 제대로 동작합니다.
alert( "다시 호출: " + worker.slow(3, 5) ); // 동일한 결과 출력(캐시된 결과)
func.apply
func.call(this,... argument) 대신 func.apply(this, arguments)를 사용할 수 있다.
func.call(context, ...args); // 전개 문법을 사용해 인수가 담긴 배열을 전달하는 것과
func.apply(context, args); // call을 사용하는 것은 동일합니다.
인수가 이터러블 => call
인수가 유사 배열 => apply
context와 함께 인수 전체를 다른 함수에 전달하는 것을 call forwarding이라고 한다.
가장 간단한 형태의 call forwarding
let wrapper = function() {
return func.apply(this, arguments);
};
기존 함수 func 호출과 명확하게 구분됨.
메서드 빌리기
function자체에서 다룰 수 있는 인수가 한정되어 있고, 그를 확장해서 쓰고 싶을 경우.
[].join : 일반 배열로부터 join 메서드를 빌려,
[].join.call : argument를 컨텍스트로 고정해 join 메서드를 호출하여 사용 가능.
데코레이터와 함수 프로퍼티.
- 데코레이터를 사용할 함수에 func.calledCount 등의 프로퍼티가 있으면 데코레이터를 적용항 함수에선 프로퍼티 사용 불가.
객체 메서드가 객체 내부가 아닌 다른 곳에 전달되어 호출되면 this가 사라진다.
해결 방법 1) 래퍼 Wrapper function 사용
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
//wrapper function
setTimeout(function() {
user.sayHi(); // Hello, John!
}, 1000);
해결 방법 2) bind 사용
모든 함수에 내장되어 있는 내장 메서드 bind사용.
// 더 복잡한 문법은 뒤에 나옵니다.
let boundFunc = func.bind(context);
** bind의 인수는 func에 그대로 전달된다.
let user = {
firstName: "John"
};
function func(phrase) {
alert(phrase + ', ' + this.firstName);
}
// this를 user로 바인딩합니다.
let funcUser = func.bind(user);
funcUser("Hello"); // Hello, John (인수 "Hello"가 넘겨지고 this는 user로 고정됩니다.)
** bindAll : 객체에 복수의 메서드가 있을 때 사용가능.
부분 적용
this 말고 다른 인수도 바인딩 가능.
let bound = func.bind(context, [arg1], [arg2], ...);
bind 사용 시엔 context를 꼭 넣어야 함. this와 같은 context가 불필요할 경우 null을 넘겨줄 것.
function mul(a, b) {
return a * b;
}
let triple = mul.bind(null, 3);
alert( triple(3) ); // = mul(3, 3) = 9
alert( triple(4) ); // = mul(3, 4) = 12
alert( triple(5) ); // = mul(3, 5) = 15
위의 예제는 context가 필요 없으므로 null을 넣어주고, 첫 번째 인자로 3을 인수로 넣어 mul함수에 bind시킴.
위와 같은 부분 함수 사용 이유?
: send(from, to, text)와 같은 함수가 있을 때, 객체 user안에서 부분 적용을 활용하면, 전송 주체를 현재 사용자로 고정한 sendTo(to, text) 구현 가능.
- this를 갖지 않는다.
- arguments를 지원하지 않는다. (arguments == 유사 배열 객체)
** 유사 배열 객체? length속성, 인덱스는 있지만, forEach, map과 같은 내장 메서드는 없음.
- new와 함께 호출할 수 없다. (this가 없기 때문에 생성자 함수로 사용할 수 없음.)
- super 없음.
javascript.info - 프로토타입과 프로토타입 상속 (0) | 2022.12.30 |
---|---|
javascript.info - 객체 프로퍼티 설정 (0) | 2022.12.30 |
javascript.info - 함수 심화학습 (0) | 2022.12.29 |
javascript.info - 자료구조와 자료형 (0) | 2022.12.28 |
javascript.info - 객체 기본(2) (0) | 2022.12.27 |