[JavaScript]코딩앙마 중급(6)Closure, setTimeout, setInterval, call, apply, bind

Joy Ming·2023년 7월 11일

코딩앙마

목록 보기
8/13

클로저(Closure)

어휘적 환경(Lexical Environment)

코드가 실행되면 스크립트 내에서 선언한 변수들이 렉시컬 환경에 올라감.

let 으로 선언한변수도 호이스팅 되지만 초기화가 안됨. 노란색 위치에서 사용 불가.

함수선언문은 변수와 달리 바로 초기화됨.
그러므로 저 노란색 위치에서도 사용가능.
(변수에 할당한 함수표현식은 안됨)

노란 부분까지 실행했을때 let one은 초기값 undefined를 비로소 갖음.
사용해도 에러는 발생하지 않음.

새로운 렉시컬 환경이 만들어짐
함수가 넘겨받은 매개변수와 지역변수들이 저장됨.

함수가 호출되는 동안 함수에서 만들어진 내부렉시컬환경과 외부에서 받은 전역렉시컬환경 두개를 가짐.

내부렉시컬환경은 외부 렉시컬환경에 대한 참조를 가짐
지금은 저 함수의 외부 렉시컬 환경이 전역렉시컬환경임.

코드에서 변수를 찾을때 내부에서 찾고,
없으면 외부, 거기에도 없으면 전역 렉시컬 환경에서 까지 범위를 넓혀서 찾음.

위 코드에서 one, num은 일단 내부 렉시컬 환경에서 찾음.
num은 찾았으나 one없음

그러면 외부로 넓혀 찾음. 찾았으니 더해줌

예시 하나더


최초 실행시 makeAdder와 add3은 전역렉시컬 환경에 들어감.

노란색 코드가 실행될때 makeAdder가 실행되고 makeAdder 렉시컬 환경이 만들어짐.
여기서 전달받은x의 값이 makeAdder렉시컬 환경에 들어감.
(함수의렉시컬환경에는 넘겨받은 변수와 지역변수들이 저장됨)

초기화 되지 않았던 add3은 함수가 실행됐으니 리턴하는 함수가 됨

노란색 부분을 실행하면 저 표시 부분 함수가 실행되는데 이때 또 렉시컬 환경이 만들어짐


노란색 코드를 실행할때 add3렉시컬환경에서 y=2 는 있는데 x가 없으니 외부 렉시컬 환경에서 x를 찾음. 찾았으니 실행

정리



add3 함수가 생성된 이후에도 상위함수인 makeAdder의 x에 접근 가능,
이런걸 closure 클로저 라고 함

클로저란 함수과 그 함수의 렉시컬 환경의 조합
함수가 생성될 당시의 외부 변수를 기억하고
생성된 이후에서 그 외부 변수에 계속 접근이 가능한 기능.

외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있음.

makeAdder(10)이 호출 되지만,
add3(1)에는 아무런 변화가 없음.
add10과 add3은 서로 다른 환경을 가짐.

예제



은닉화에 성공, 갑자기 99로 바꾸거나 100씩 증가하거나 불가.

setTimeout


두개의 매개변수를 받음.
일정시간이 지난뒤 실행하는 함수, 시간


함수이름 말고 직접 코드를 작성해도 동일하게 동작함.

인수가 필요하다면 시간뒤에 적어준다.
mike는 함수의 첫번째 인수로 전달됨

clearTimeout(tId)는 예정된 작업을 없앰.
setTimeout은 timeID를 반환하는데
이것을 이용하면 스케줄링을 취소할 수 있음.

function showName(name){
    console.log(name);
}
const tId = setTimeout(showName,3000,'Mike'); 
clearTimeout(tId);

3초가 지나기전에 저 코드(clearTimeout)가 실행되므로,
아무일도 일어나지 않음.

+)chatGPT설명추가
setTimeout 함수는 실행 후 생성된 타이머 식별자(ID)를 반환합니다. 이 식별자를 clearTimeout 함수에 전달하면 예약된 실행이 취소됩니다.

따라서, const tId = setTimeout(showName, 3000, 'Mike') 코드는 showName 함수를 3초 후에 실행하도록 예약하고, 생성된 타이머 식별자를 tId 변수에 저장합니다.

clearTimeout(tId)는 tId로 지정된 타이머 예약을 취소합니다. 이 경우, setTimeout에 의해 예약된 실행이 취소되어 showName 함수는 실행되지 않습니다.

즉, clearTimeout 함수를 호출함으로써 showName 함수의 실행 예약이 취소되고, 'Mike'가 콘솔에 출력되지 않습니다.

  • 타이머 식별자
    타이머 식별자(ID)는 setTimeout 또는 setInterval 함수를 호출할 때 반환되는 값입니다. 이 식별자는 생성된 타이머를 고유하게 식별하고 관리하는데 사용됩니다.

타이머 식별자는 숫자로 표현되며, 일반적으로 변수에 할당하여 나중에 타이머를 취소하거나 제어하는 데 사용됩니다. setTimeout 함수는 실행 예약을 위해 타이머를 생성하고, setInterval 함수는 일정한 간격으로 반복 실행하기 위해 타이머를 생성합니다.

예를 들어, 다음과 같은 코드를 생각해보세요:

const timerId = setTimeout(function() {
  console.log("타이머 실행");
}, 3000);

위 코드에서 setTimeout 함수는 3초 후에 콘솔에 메시지를 출력하는 타이머를 생성합니다. setTimeout은 생성된 타이머의 식별자(ID)를 timerId 변수에 할당합니다. 이후에 timerId를 사용하여 타이머를 제어하거나 취소할 수 있습니다.

타이머 식별자는 clearTimeout 또는 clearInterval 함수를 호출할 때 이용하여 예약된 실행을 취소하거나 반복 실행을 중지할 수 있습니다.

  • setTimeout 콘솔창

    setTimeout을 콘솔에 입력하면 위와 같이 숫자가 출력되는데 그게 타이머 식별자.
    저 숫자를 clearTimeout의 인자로 전달하면 타이머를 취소할 수 있음.

+)변수에 함수를 할당했을때
할당한 함수에 이름이 있더라도 (sol)
밖에서 따로 호출 불가함.

let a = function sol(){
    console.log('hi');
}

sol();//에러 

setInterval

setTimeout과 사용법 동일
한번 실행하고 끝내는 것과 달리 이건 계속 반복 수행함.

3초마다 마이크가 찍힘.
중간에 중단하려면 clearInterval(tId)를 실행하면 됨

주의사항


setTimeout에서 시간0으로 두어도
아래 코드의 콘솔부터 찍힘
이유는 현재실행중인 스크립트가 종료된 이후 스케줄링함수를 실행하기 때문
그리고 브라우저는 기본적으로 4ms정도의 대기시간이 있음. 0이라 적어도 4ms혹은 그 이상이 걸릴 수 있단 뜻.

예제


메시지가 1초에 한번씩 나옴
(근데 num++말고 ++num을 해야할듯)

  • 계속말고 5초동안만 보여주고 싶다면?

+)근데 여기서 궁금한거

let num=0;

function showTime(){
    console.log(`접속 ${++num}초 지났어요`);
}

if(num>5){
    clearInterval(tid);
}
let tid = setInterval(showTime,1000);

왜 clearInterval(tid)를 함수안에 넣지 ? 하는 의문이 있어서 저렇게 바깥에다가 코딩했더니 적용안됨.
왜인고 하니 생각해보니 setInterval 부분 실행될때 아래 if문까지 차례가 안내려오겠다.했음

다음 췟gpt답
+)if문이 실행되지 않는 이유는 if문이 setInterval 함수 호출 이후에 작성되어 있기 때문입니다. setInterval 함수는 일정한 간격으로 showTime 함수를 실행하는 반면, if문은 초기에 실행될 때 num의 값이 0이기 때문에 조건을 만족하지 않습니다.

함수의 this값을 지정하는 call, apply, bind

call

  • 자바스크립트는 일반적인 방법 외에도 함수를 어디서 어떻게 호출했느냐와 관계없이 this가 무엇인지 지정할 수있음.
  • call메소드는 모든함수에서 사용될 수 있으며, this로 특정값을 지정할 수 있음.

예제


위와 같이 showThisName을 호출하면 아무것도 안뜲.
저기서 this가 가르키는것 = window

window.name이 위와 같이 빈문자열이기에
아무것도 안뜨는것.

그냥 호출 말고 call해주고 mike넣어주면
'Mike'가 찍힘

call을 사용하고 this로 사용할 객체를 넘기면
주어진 객체의 메소드처럼 사용할 수 있다.

call의 첫번째 매개변수는 함수내부에 있는 this로 사용할 값(대체할값?)
매개변수가 더 있으면 그 매개변수로 호출하는 함수로 전달됨.

객체의 정보가 업데이트 됨.

update.call(mike, 1999, 'singer)은
update함수를 실행하는데
this를 mike라는 객체로 넣어달란 뜻.
그리고 update함수에 들어갈 매개변수 1999랑 singer도 넣어준거.

apply

함수 매개변수를 처리하는 방법을 제외하면 call과 완전히 같음.
call은 일반적인 함수와 마찬가지로 매개변수를 직접받지만, apply는 매개변수를 배열로 받음.

apply는 배열원소를 함수 매개변수로 사용할때 유용함.

위 Math.min코드에서 인자를 배열로 넣어버리면 NaN이 나옴.

Math.min([1,2,3]);//NaN

spread연산사 사용하면 나옴

console.log(Math.min(...[1,2,3]));//1

apply는 두번째 매개변수로 배열을 전달하면 그요소들을 차례대로 인수로 사용함.

const nums=[3,4,2,5,1,2,56];
const minNum= Math.min.apply(null, nums);
const maxNum= Math.max.apply(null,nums);
console.log(minNum, maxNum);//1 56

Math.max는 this가 필요하지 않아서 아무값이나 넣은거임.

const minNum= Math.min.call(null, ...nums);

call로는 스프레트 연산자 사용해서 가능

bind

함수의 this값을 영구히 바꿀 수 있음.
this값을 바꿀 수 있는 마지막함수.

위 함수 update에서 this값을 영원히 mike로 하고 싶으면? bind를 사용

이 bind는 새로 바인딩한 함수를 하나 만듦.
이함수는 항상 mike를 this로 받음.


function intro(name, age){
    this.name=name;
    this.age=age;
}

let user ={
    tall :180,
}

// console.log(intro.call(user, 'mike', 22));//undefined
//console.log(intro.bind(user));//[Function: bound intro]

// console.log(mike);//{ tall: 180, name: 'mike', age: 22 }

let a = intro.bind(user);

//위 한줄과 같은 거
// let a = function(name, age){
//     user.name=name;
//     user.age=age;
// }

a('mike', 22);
console.log(user)//{ tall: 180, name: 'mike', age: 22 }

실사용예제


fn 호출했더니 hello만 나옴.
fn에 할당할때 this를 잃어버린것.
메소드는 user.showName()에서 점앞에있는 user가 this임.
호출할때 fn만 호출하니까 this가 없음.
이럴 때 call을 사용해서 this로 사용할 값을 넣어줄 수 있음.

fn.call(user);//hello, Mike

apply로 해도 동일

fn.apply(user)//hello, Mike
  • bind를 이용해 새 함수만들기어 호출하기
profile
Back-end

0개의 댓글