[모던JS: Core] 함수 심화 (4)

KG·2021년 5월 16일
0

모던JS

목록 보기
10/47
post-thumbnail

Intro

본 포스팅은 여기에 올라온 게시글을 바탕으로 작성되었습니다.
파트와 카테고리 동일한 순서로 모든 내용을 소개하는 것이 아닌, 몰랐거나 새로운 내용 위주로 다시 정리하여 개인공부 목적으로 작성합니다.
중간중간 개인 판단 하에 필요하다고 생각될 시, 기존 내용에 추가로 보충되는 내용이 있을 수 있습니다.

setTimeout & setInterval

자바스크립트에서 일정 시간 경과 후 원하는 함수를 실행하는 방법엔 크게 두 가지 호출 스케줄링 방식이 있다.

  1. setTimeout 일정 시간 경과 후 함수 실행
  2. setInterval 일정 시간 간격으로 함수 실행

자바스크립트 명세에는 setTimeoutsetInterval이 명시되어 있지 않으나 시중에 있는 모든 브라우저 및 Node.js 환경 대부분이 이를 지원한다.

1) setTimeout

실행하고자 하는 함수를 일정 delay 시간 후에 호출할 수 있다. 이때 delay의 단위는 밀리초이다.

function sayHi () {
  console.log('hi');
}

setTimeout(sayHi, 1000);	// 1초후 실행

// 함수에 인수를 넘겨줄 수 있다.
function sayHello (who, phrase) {
  console.log(`${who}, ${phrase}`);
}

setTimeout(sayHello, 1000, 'KG', 'Hello');

// 익명 함수 전달
setTimeout(() => console.log('hi'), 1000);

setTimeout을 호출하게 되면 타이머 식별자(Timer Identifier)가 반환된다. 만약 스케줄링을 취소하고자 하면 해당 식별자를 사용해서 취소시킬 수 있다.

const timerId = setTimeout(...);
clearTimeout(timerId);                           

2) setInterval

setInterval 은 실행하고자 하는 함수를 일정 delay 간격으로 계속 호출하고자 할 때 사용한다. 문법은 setTimeout과 동일하다.

역시 해당 스케줄링을 취소시키려면 반환된 타이머 식별자로 하여 claerInterval을 호출할 수 있다.

// 2초 간격으로 alert창 출력
const timerId = setInterval(() => alert('hi'), 2000);

// 5초후 스케줄링 취소
setTimeout(() => clearInterval(timerId), 5000);

대부분 브라우저는 alert/prompt/confirm 창이 떠 있는 동안에도 내부 타이머를 멈추지 않는다. 이는 타이머가 자바스크립트 내부에서 이벤트루프를 통해 다른 외부 API에 위임되기 때문인데 자세한 내용은 다음 챕터에서 다루도록 하자. 따라서 해당 창이 떠 있는 동안에 계속 시간이 흐르고 있으므로 창을 닫고 2초 뒤에 실행이 아니라 그 보다 짧은 간격으로 실행될 수 있다.

3) 중첩 setTimeout

어떤 작업을 일정 간격을 두고 실행하는 방법에는 크게 2가지가 있다.

  1. setInterval
  2. 중첩 setTimeout

중첩 setTimeout을 이용하는 방법은 setInterval을 사용하는 방법보다 유연하다. 호출 결과에 따라 다음 호출을 원하는 방식으로 조정해 스케줄링이 가능하기 때문이다.

let delay = 5000;

let timerId = setTimeout(function request() {
  ...
  
  if (서버 과부화로 인한 요청 실패){
    // 요청 딜레이 증가
    delay *= 2;
  }
  // 중첩으로 setTimeout 호출
  // 증가된 delay로 서버 과부화에 따라 요청 간격 증가
  timerId = setTimeout(request, delay);
  
}, delay);

setTimeout에 전달되는 함수 request는 그 내부에서 또 다시 setTimeout을 호출하고 있다. 즉 재귀호출인데 별도의 탈출조건이 보이지 않는 무한루프와도 같아 보인다. 하지만 setTimeout은 한 번 호출되면 전달받은 함수와 딜레이를 이벤트루프를 타고 다른 큐에 전달하고 종료된다. 때문에 가비지 컬렉터에 의해 수집되어 메모리 낭비를 어느 정도 방지할 수 있다. 반면 setInterval의 경우에는 clearInterval이 호출되기 전까지는 함수에 대한 참조가 메모리에 유지된다.

setInterval이나 setTimeout에 함수를 넘기면, 함수에 대한 내부 참조가 새롭게 만들어지고 이 참조 정보는 스케줄러에 저장된다. 따라서 해당 함수를 참조하는 것이 없어도 setIntervalsetTimeout에 넘긴 함수는 가비지 컬렉션의 대상이 되지 않는다.

하지만 이러한 동작 방식에는 부작용이 하나 있는데, 만약 외부 렉시컬 환경을 참조하는 함수가 있다면 이 함수가 메모리에 남아있는 동안엔 외부 변수 역시 메모리에 남아있게 된다. 따라서 실제 함수가 차지했어야 하는 공간보다 더 많은 메모리 공간이 사용되기에, 이를 방지하기 위해서는 더 이상 스케줄링 할 필요가 없어진 함수는 아무리 작더라도 취소해야 한다.

이처럼 CPU 소모가 많은 작업등을 주기적으로 수행하는 경우엔 setTimeout을 재귀 실행하는 방법이 유용하다. 작업에 걸리는 시간에 따라 다음 작업을 유동적으로 계획할 수 있기 때문이다.

또한 중첩 setTimeout을 이용하는 방법은 지연 간격을 보장하지만 setInterval의 경우에는 이를 보장할 수 없다.

let i = 1;
setInterval(function () {
  func(i++);
}, 100);

setInterval을 사용한 예시에서는 내부 스케줄러가 func(i++)를 100밀리초마다 실행한다.

이때 setInterval을 사용하면 func 호출 사이의 지연 간격이 실제 명시한 100ms보다 짧아진다. 왜냐하면 func실행하는데 소요되는 시간도 지연 간격에 포함하기 때문이다. 만약 func가 명시한 딜레이보다 더 긴 시간동안 수행된다면 엔진이 func의 실행이 종료될 때까지 기다려주고, 종료된것을 확인하면 바로 다음 호출을 시작한다.

반면 중첩 setTimeout을 사용하는 경우엔 명시한 지연 시간이 보장된다.

let i = 1;
setTimeout(function run() {
  func(i++);
  setTimeout(run, 100);
}, 100);

이는 이전 함수의 실행이 종료된 이후에 다음 함수 호출에 대한 스케줄링 계획이 새롭게 생성되기 때문이다.

4) 대기시간이 0인 setTimeout

문법에 따라 setTimeout의 딜레이를 0으로 설정하거나 아예 딜레이를 인수로 전달하지 않는다면 대기 시간을 0으로 설정하는 것과 동일하다.

따라서 대기 시간없이 즉각적으로 실행할 수 있을 것 같지만, 이는 사실상 실행할 수 있는 시점에서 가능한 빨리 실행하는 의미와 동일하다.

setTimeout(() => console.log("First"));

console.log("Second");

// 출력 결과는 아래의 순서와 같다.
// Second
// First 

setTimeout이 먼저 실행되지만 이는 스케줄링에 해당 함수를 기록하는 작업이고, 현재 작업인 console.log("Second")까지 모두 실행을 마치고 나서야 스케줄링 된 함수가 실행된다. 이와 관련된 자세한 사항은 다음 챕터에서 다루도록 하자.

이 같은 이유로 스케줄링 메서드를 사용할 때는 명시한 지연 간격이 항상 보장되지 않을 수 있다. 아래와 같은 상황에서 브라우저 내 타이머가 느려지는 상황이 만들어지면 지연 간격이 보장되지 않는다.

  • CPU가 과부하 상태인 경우
  • 브라우저 탭이 백그라운드 모드인 경우
  • 노트북이 배터리에 의존해서 구동 중인 경우

브라우저 환경에서는 실제 대기시간이 0이 아니다. HTML5표준에 따르면 다섯번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다는 제약이 명시되어 있다. 반면 서버 측에는 이러한 제약이 없다.

데코레이터 & 포워딩

자바스크립트는 함수 지향 언어로 불리우는만큼 함수를 다룰 때 탁월한 유연성을 제공한다. 함수는 값으로서 이곳 저곳 전달이 가능하고, 객체로서 사용될 수도 있다.

1) 코드 변경 없이 캐싱 기능 추가

CPU를 많이 잡아먹지만 결과는 안정적인 함수 slow(x)가 있다고 가정해보자. 결과가 안정적이라는 것은 side effect없이 입력 x가 같으면 항상 동일한 출력을 만든다는 것과 같은 의미이다.

만약 slow(x)가 자주 호출된다면 호출마다 많은 양의 시간을 CPU연산에 처리할 것이다. 하지만 동일한 입력일 경우 동일 출력이 보장되므로, 만약 동일한 입력이 다시 들어온다면 이전에 계산한 값을 그대로 활용할 수 있다. 이러한 캐싱 관련 기능을 slow() 함수 내부에 자체적으로 구현할 수 있지만 래퍼 함수를 별도로 만들어 캐싱 기능을 추가해보자.

function slow(x) {
  // 오래 소요되는 CPU 작업 수행
  console.log(`result : ${x}`);
  return x;
}

// 함수를 인수로 전달받는 캐싱 데코레이터 래퍼 함수
function cachingDecorator(func) {
  let cache = new Map();	// 캐시를 생성
  
  // x를 인수로 하는 함수 반환
  return function(x) {
    if (cache.has(x)) {		// 현재 값이 캐시에 있다면
      return cache.get(x);	// 바로 값을 반환
    }
    
    let result = func(x);	// 그렇지 않은 경우
    				// 함수를 호출하고
    cache.set(x, result);	// 결과를 캐시에 저장
    return result;
  };
}

slow = cachingDecorator(slow);

console.log( slow(1) );	// 1을 계산하고 캐시에 저장
console.log( slow(1) );	// 저장된 1을 반환

cachingDecorator와 같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 데코레이터(Decorator)라고 한다.

아래 그림에서 볼 수 있듯이 cachingDecorator(func)를 호출하면 래퍼 함수 function(x)가 반환된다. 래퍼 함수는 func(x)의 호출 결과를 캐싱 로직으로 감싸는 역할을 수행한다.

외부에서 보았을 때 함수 slow는 래퍼로 감싼 이전이나 이후나 동일하게 동작하며, 내부적으로 캐싱 기능이 추가되었을 뿐이다. 함수 본문을 수정하는 것보다 이처럼 독립된 래퍼 함수를 사용할 때 생기는 이점은 다음과 같다.

  • cachingDecorator를 재사용 가능
  • 캐싱 로직이 분리되어 slow 함수 자체의 복잡성이 증대되지 않음
  • 필요 시 여러개의 데코레이터를 조합하여 사용 가능

2) func.call

위에서 구현한 캐싱 데코레이터는 객체 메서드에 사용하기엔 적합하지 않다. 객체 메서드에 경우엔 데코레이터를 적용해도 제대로 동작하지 않는다.

const worker = {
  someMethod() {
    return 1;
  }
  
  slow(x) {
    // 집약적인 CPU 작업 수행
    console.log('slow 호출');
    return x * this.someMethod();
  }
};

worker.slow(1);	// 1

worker.slow = cachingDecorator(worker.slow);

worker.slow(2);	// Error!

그 이유는 래퍼 함수에서 기존 함수 func(x)를 호출할때 this의 정보가 undefined가 되기 때문이다. 앞서 살펴보았듯이 자바스크립트의 this는 런타임에 결정되기 때문에 항상 점 앞의 객체에 따라 정의된다.

하지만 데코레이터의 래퍼 함수 내부에서는 let result = func(x)와 같이 바로 객체 메서드를 호출하고, 호출된 메서드 내부에서 this에 접근하고 있지만 이때 thisundefined가 되므로 에러가 발생한다. 즉 아래와 동일한 상황이다.

let func = worker.slow;
func(2);	// Error - this의 컨텍스트 실종

객체의 메서드는 호출할 때 this를 내부적으로 바꾸어주지만, 위와 같이 객체 메서드를 꺼내어 할당하면 이는 더 이상 객체 메서드가 아닌 일반 함수로 동작한다. 따라서 이 경우 this의 컨텍스트가 실종된다. (비엄격모드에선 window, 엄격모드에선 undefiend)

따라서 에러가 발생하지 않게 수정하기 위해서는 this를 명시적으로 고정해 함수를 호출할 수 있게 하는 방식을 취할 수 있다. 이때 사용하는 내장 함수 메서드가 call(...) 이다.

func.call(context, arg1, arg2, ...);

위와 같이 메서드 호출 시, 첫 번째 인수가 this가 되고 이어지는 인수들은 func에 전달되는 인수가 되어 최종적으로 맨 앞단의 func 함수가 호출된다. 따라서 앞서 살펴본 예시를 다음과 같이 수정할 수 있다.

// 객체 메서드 프로퍼티로 sayHi 함수를 지정
const a = { name: 'aaa' };
const b = { name: 'bbb' };

function sayHi () {
  alert(`hi ${this.name}`);
}

a.func = sayHi;
b.func = sayHi;

a.func();	// 'aaa'
b.func();	// 'bbb'

// 내장 함수 메서드 call 사용
sayHi.call( a );	// 'aaa'
sayHi.call( b );	// 'bbb'

따라서 위에서 선언한 데코레이터를 call을 사용해서 수정한다면 에러를 잡을 수 있다.

function cachingDecorator(func) {
  ...
  return function(x) {
    ...
    let result = func.call(this, x);
    ...
  };
}

// 캐싱 데코레이터 적용
worker.slow = cachingDecorator(worker.slow);
  
worker.slow(2);	// 제대로 동작
worker.slow(2);	// 제대로 동작, 캐싱된 값 반환

위와 같이 수정하게 되면 다음의 과정을 거친다.

  1. 데코레이터 적용 후 worker.slow 메서드는 래퍼 함수 function (x) { ... }이 된다.
  2. worker.slow(2)를 실행하면 래퍼는 2를 인수로 받고, this는 점 앞에 위치한 worker가 된다.
  3. 따라서 func.call(this, x)에서 thisworker가 전달되고 x에는 2가 원본 메서드에 전달된다.

3) func.apply

만약 위에서 사용한 데코레이터가 여러 개의 인수를 받아야 한다면 어떻게 해야할까? 먼저 여러개의 인수를 고유값으로 처리할 수 있는 별도의 해시 알고리즘이 필요하고, 또한 여러개의 인수를 전달받아야 하기에 arguments 객체를 이용해 모든 인수를 참조하여 이를 다시 나머지 매개변수로 전달하는 등의 과정이 추가로 필요할 것이다.

...
return function() {
  let key = hash(arguments);
  if (cache.has(key)) {
    return cache.get(key);
  }
  
  let result = func.call(this, ...arguments);
  ...
}

하지만 이러한 과정을 일일이 추가하지 않고 간단하게 func.apply를 사용해 해결할 수 있다.

func.apply(context, args);

applyfuncthiscontext로 고정해주고, 유사 배열 객체인 args를 인수로 사용할 수 있게 한다. 즉 callapply의 문법적인 차이는 call의 경우 복수 인수를 따로따로 받는 대신에 apply는 인수를 유사 배열 객체로 한번에 받는다는 점 뿐이다. 따라서 다음과 같이 동일하게 두 메서드를 사용할 수 있다.

func.call(context, ...args);	// 전개연산자 사용
func.apply(context, args);	// 위와 동일한 결과

물론 약간의 차이는 있다.

  • 전개 연산자 ...Iterableargs 객체를 분해
  • apply는 오직 유사 배열 형태의 args만 허용

배열같이 Iterable 이면서 동시에 유사 배열인 객체엔 둘 모두 사용이 가능하다. 그러나 대부분 자바스크립트 엔진은 내부에서 apply를 최적화하기 때문에 apply가 성능적으로 조금 더 빠른 경우가 많다.

이와 같이 컨텍스트(context)와 함께 인수 전체를 다른 함수에 전달하는 것을 콜 포워딩(Call Forwarding)이라고 한다.

4) 메서드 빌리기

다음과 같은 해싱 함수가 있을 때, 해당 함수는 오직 두 개의 인수만을 다룰 수 있다. 만약 args의 요소 개수에 상관없이 요소들을 합칠 수 있다면 조금 더 범용성이 넓은 함수가 될 것 이다.

function hash (args) {
  return args[0] + ',' + args[1];
}

// join을 사용하여 몇 개의 인수 상관없이 문자열로 변환?
function hash (args) {
  return args.join();
}

그러나 join을 이용하는 방법은 동작하지 않는다. 왜냐하면 hash함수를 호출할 때 인수로 넘겨지는 args는 진짜 배열이 아닌 Iterable 객체이거나 유사 배열이기 때문에 배열의 내장 메서드를 사용할 수가 없다.

이때 다음과 선언한다면 배열 메서드를 빌려올 수 있다. 이는 배열 메서드인 join이 인수로 전달받은 유사 배열 객체인 argumentsthis에 할당하여 동작하기에 정상적으로 연산이 가능하다.

function hash () {
  return [].join.call(arguments);
  // 또는 return Array.prototype.join.call(arguments);
}

함수 바인딩

앞서 객체 메서드가 일반 함수에 할당이 되거나, 객체 메서드를 콜백으로 전달하는 경우엔 this 정보가 실종되는 것을 살펴보았다.

let user = {
  firstName: "SL",
  sayHi() {
    console.log(`Hello, ${firstName}!`);
  }
}

setTimeout(user.sayHi, 1000);	// Hello, undefined!

브라우저에서 setTimeout은 인수로 전달받은 함수를 호출할 때 thiswindow를 할당한다. 때문에 정상적으로 객체 내에 firstName에 접근하지 못해 undefined를 출력하게 된다.

1) 래퍼함수

래퍼 함수를 사용하면 위와 같은 부작용을 막을 수 있다.

let user = { ... };
            
setTimeout(function() {
  user.sayHi();
}, 1000);

// 또는 화살표함수 이용
setTimeout(() => user.sayHi(), 1000);

이는 외부 렉시컬 환경에서 user를 받아서 보통 때처럼 메서드를 호출했기 때문에 정상적으로 결과를 호출한다. 즉 객체 메서드 앞에 있는 객체 userthis가 되기 때문이다.

그러나 이 방식에는 취약점이 있는데, 만약 setTimeout이 트리거 되기 전에 user 객체의 값이 변경되는 경우엔, 변경 이후의 메서드를 호출하게 된다.

// (2) 1 출력
setTimeout(() => user.sayHi(), 1000);

// (1) 1초가 지나기 전에 user 값 변경
user = { sayHi() { console.log(1); } };

2) bind

모든 함수는 this를 수정하게 해주는 내장 메서드 bind를 제공한다. 이 방법을 이용하면 위에서 살펴본 부작용 또한 방지할 수 있다.

let boundFunc = func.bind(context);

func.bind(context)는 함수처럼 호출이 가능한 특수 객체를 반환하고, 이 객체를 호출하면 thiscontext로 고정된 함수 func가 반환된다.

let user = {
  firstName: 'SL'
};

function func() {
  console.log(this.firstName);
}

let funcUser = func.bind(user);
funcUser();	// 'SL'

이때 bind는 함수의 this 값만 새로운 컨텍스트로 교환하고 기존에 전달받는 인수에는 영향을 끼치지 않는다. 따라서 인수는 원본 함수에 그대로 전달된다.

let user = { ... };

let sayHi = user.sayHi.bind(user);

// 객체 없이도 일반 함수처럼 호출 가능
sayHi();	// Hello, SL

// (2) 변경 전 기존 값을 호출
setTimeout(sayHi, 1000);	// Hello, SL

// (1) user 객체의 내부 값을 변경해도
user = {
  firstName: 'KG',
};
            

이처럼 객체 메서드에 bind를 적용하면 sayHi는 이제 묶인 함수가 되어 단독으로 호출이 가능하다.

3) 부분 적용

bind 메서드는 this 뿐만 아니라 인수도 바인딩이 가능하다.

function mul (a, b) {
  return a * b;
}

위와 같이 두 수의 곱을 반환하는 함수가 있을때 bind를 사용해 인수를 바인딩하여 사용할 수 있다.

let double = mul.bind(null, 2);

double(2);	// 4
double(3);	// 6
double(4); 	// 8

이처럼 thisnull로 바인딩하고 인수로 2를 전달하게 되면 원본 함수 mul의 인수 a2로 고정된 효과를 갖는다. 추가 인수는 그대로 전달되어 함수 본문을 실행한다. 이러한 방식을 부분 적용(partial application)이라고 부른다. 위 예시에서는 this를 사용하지 않고, bind 함수는 항상 this에 넘겨줄 컨텍스트를 지정해주어야 하므로 null을 넘겨주었지만 다른 컨텍스트를 넘겨주어도 무방하다.

이처럼 부분 적용을 통해 부분 함수를 만든다면 가독성이 좋은 독립 함수를 만들 수 있다. 또한 인수를 고정하기 때문에 매번 여러개의 인수를 일일히 전달할 필요가 없다는 이점 또한 있다.

4) 컨텍스트 없는 부분 적용

앞서 bind 메서드는 항상 컨텍스트 정보를 넘겨주어야 한다고 했다. 때문에 위의 예시에서는 this를 사용하지 않아 null 값을 넘겨주었다. 만약 인수 일부는 고정하고 컨텍스트 this는 따로 고정하고 싶지 않은 경우엔 네이티브 bind만으로는 구현하기 어렵다. 이 경우 부분 적용을 통해 인수만 바인딩 해주는 헬퍼 함수를 손쉽게 구현할 수 있다.

function partial (func, ...argsBound) {
  return function(...args) {
    return func.call(this, ...argsBound, ...args);
  }
}

let user = {
  firstName: "John",
  say(time, phrase) {
    alert(`[${time}] ${this.firstName}: ${phrase}!`);
  }
};

// 시간을 고정한 부분 메서드를 추가
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());

user.sayNow("Hello");

partial(func[, arg1, arg2, ...])을 호출하면 래퍼 함수가 반환되고, 해당 래퍼 함수를 호출하면 다음과 같은 방식으로 동작한다.

  • 동일한 this를 전달 (user.sayNowuser를 대상으로 호출)
  • partial을 호출할 때 받은 인수(시간 관련)는 ...argBound에 전달
  • 래퍼에 전달된 인수("Hello")는 ...args에 전달

화살표 함수

앞서 화살표 함수에 대해 간단하게 보았을 때, 화살표 함수는 고유한 this를 가지지 않는다고 말한 바 있다. 별도의 this 대신 외부 컨텍스트에 있는 this에 접근한다.

자바스크립트에서 함수는 값으로 전달되는 콜백형식을 많이 취하기 때문에 저 멀리 동떨어진 곳에서 실행될 작은 함수를 작성해야 할 경우가 종종 있다.

  • arr.forEach(func) - funcforEach가 호출될 때 배열 arr의 요소 전체를 대상으로 실행
  • setTimeout(func) - func는 내장 스케줄러에 의해 실행
  • ...

1) this가 없는 화살표 함수

화살표 함수에는 this가 없기 때문에 화살표 함수 본문에서 선언된 this는 외부에서 값을 가져오게 된다. 이런 특징은 객체의 메서드 안에서 동일 객체 프로퍼티를 대상으로 순회할 때 유용하게 사용할 수 있다.

let group = {
  title: '1모둠',
  students: ['KG', 'SJ', 'TW'],
  
  showList() {
    this.students.forEach(
      student => console.log(this.title + ' : ' + student)
    )
  },
};

group.showList();
                          

forEach 내부에서 선언된 this는 화살표 함수 본문에 있기 때문에 외부에 있는 this에 접근한다. 즉 showList가 가리키는 대상과 동일하다. 만약 화살표 함수가 아닌 일반 함수를 사용했다면 에러가 발생했을 것이다.

화살표 함수는 new와 함께 실행할 수 없다. this가 없기 때문에 생성자 함수로 사용할 수가 없는 것이다.

2) arguments가 없는 화살표 함수

화살표 함수는 일반 함수와 다르게 모든 인수에 접근할 수 있게 해주는 유사 배열 객체 arguments가 없다. 이런 특징은 오히려 현재 this값과 arguments 정보를 함께 실어 호출을 포워딩하는 데코레이터를 만들 때 유용하게 사용된다.

function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms);
    // 화살표 함수에는 this도 arguments도 없기 때문에
    // 이는 모두 래퍼함수에서의 this와 arguments를 가리킨다
  };
}

function sayHello (who) {
  console.log('Hello', who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("KG");	// 2초후 "Hello KG" 출력

defer(f, ms)는 함수를 인자로 받고 이 함수를 래퍼로 감싸 반환하는데, 함수 fms 밀리초 이후에 호출된다.

References

  1. https://ko.javascript.info/advanced-functions
  2. https://www.zerocho.com/category/JavaScript/post/597f34bbb428530018e8e6e2
  3. https://www.zerocho.com/category/JavaScript/post/57433645a48729787807c3fd
profile
개발잘하고싶다

0개의 댓글