목차
목차1. javascript 사전 퀴즈1-1. 생성자함수와 일반 함수의 this 바인딩 차이1-2. 즉시실행함수의 결과1-3. functionscope와 this1-4. 오류가 발생하는 원인과 해결방안은?1-5. for문에서의 setTimeout 사용 시 출력값1-6. var, let , const의 차이는 무엇인가요?1-7. 클로저는 무엇인가요?
1. javascript 사전 퀴즈
1-1. 생성자함수와 일반 함수의 this 바인딩 차이
function Cat(name, age){ this.name = name; this.age = age; } const tabby1 = Cat('nana', 7); console.log(tabby1.name)
생성자함수와 일반 함수의 this 바인딩 차이
- new 키워드를 통한 생성자함수는 함수내부의 this가 선언된 객체를 가르키게 된다.
- 해당 문제는 Cat() 함수의 리턴 값이 없기 때문에,
tabby1 = undefined
가 할당되고, undefined의 name을 접근하기 때문에 오류가 발생하게 된다.
1-2. 즉시실행함수의 결과
(function(name){ console.log(`hello ${name}`) }){'roto'}
즉시실행함수를 사용하는 이유
js에서 선언된 모든 변수나 함수는 전역 공간에 저장되고, 다른 파일에서 같은 변수명을 사용할 경우 오버라이딩 되는 경우가 발생한다.
- 1. 따라서 함수스코프를 통해 이를 방지하고자, 특정 스크립트 파일이나 덩어리들을 하나의 함수로 묶어주어
변수공간을 제한
하기 위해 사용했다.
- 2. 즉시실행함수에서 특정 변수,함수만을 리턴하여, 내부의 변수를 private하게 관리할 수 있는 효과도 있다.
const logger = (function () { let privateCnt = 0; function log(msg) { console.log(msg); privateCnt += 1; } function getLogCnt() { return privateCnt; } return { log: log, getLogCnt: getLogCnt, }; })(); // logger.privateCnt에 직접 접근할 수 없게 됨
1-3. functionscope와 this
var idiots = { name: "name", genre: "genre", members: { roto: { memberName: "roto", play: function () { console.log(this.name, this.memberName); }, }, }, }; idiots.members.roto.play(); // undefined, roto // play내부의 this는 this를 실행시킨 객체인 roto : {memberName: - , play: f} 이기때문
- this는 함수 실행시 생성되는 실행컨텍스트에 속하며, 함수의 호출방법에 따라 this값이 다르다.
- 일반적으로 전역객체(windows)를 가르킴
- method 방식 호출시 this는 함수실행한 부모객체를 가르킴
- new 키워드를 통한 생성자함수로 호출한다면, 새롭게 생성된 인스턴스를 가르킴
- call / apply / bind로 지정 가능 (링크)
person2.study.call(person1)
: person2가 가지고 있는 study함수를 person1이 사용할 수 있도록 해라- call / apply
첫번째 인자로 this 지정
- 두번째 인자는 함수호출에 사용할 매개변수를 의미
- call은 매개변수의 값을 각각 받음
- apply는 매개변수를 배열형태로 받음
- 실제 사용할 수 없는 method들 사용가능
- Array.prototype.join.call(arguments)
- 함수의 매개변수인 arguments는 유사배열형태이기 때문에 바로 배열메소드 사용불가 → Array.prototype을 빌려서 배열메소드 사용가능
- bind
- 새롭게 binding된 함수를 만들고 반환
- 반환된 함수에는 this가 바뀌어 있다.
- 따라서 변수에 할당하고, 호출하는 형태로 사용한다.
let student = person2.study.bind(person1);
student()
- 화살표함수에서의 this
- 정적으로 항상 상위스코프의 this를 가르키게됨
- call, apply, bind 메소드 사용불가
1-4. 오류가 발생하는 원인과 해결방안은?
function RockBand(members) { this.members = members; this.perform = function () { setTimeout(function () { this.members.forEach(function (member) { member.perform(); }); }, 1000); }; } var sing = new RockBand([ { name: "outwater", perform: function () { console.log("sing a song~"); }, }, ]); sing.perform();
나의답변
setTimeout()의 함수 내부에서 this가 전역객체를 가르키기 때문이다.
this에 RockBand 를 연결시켜주어야 하는데, 가장 간단하게는 setTimeout의 콜백함수를 화살표함수로 바꾸어줄 수 있다.
화살표 함수는 내부적으로 this를 가지고 있지 않기 때문에, this는 스코프체인에 따라 RockBand를 가르킬 수 있게 된다.
해결방법
- 나의답변인 화살표함수
화살표함수에서의 this는 상위 스코프의 this를 바인딩하게 된다.
- bind()를 사용하여 this를 넘겨주기
bind()를 통해 현재 스코프의 this를 setTimeout 함수안으로 넘겨줄 수 있다.
setTimeout(function () { this.members.forEach(function (member) { member.perform(); }); }.bind(this), 1000);
- 클로저를 활용하여 this 넘겨주기
- 내부함수가 외부함수의 변수에 접근할 수 있도록 하는 것
- 외부의
that이라는 변수
에 현재 스코프의 this를 담아서, 내부함수가 이를 접근할 수 있도록 한다.
function RockBand(){ var that = this; this.perform = function () { setTimeout(function () { that.members.forEach(function (member) { member.perform(); }); }, 1000); }; }
1-5. for문에서의 setTimeout 사용 시 출력값
const numbers = [0, 1, 2, 3, 4]; for (var i = 0; i < numbers.length; i++) { setTimeout(function () { console.log(i, numbers[i], new Date()); }, i + 1000); }
나의답변
먼저 변수 선언 키워드 var가 블록스코프가 아닌 함수스코프를 가지기 때문입니다.
이벤트루프의 관점에서 이 코드를 바라본다면, setTimeout은 비동기 함수로, for문의 모든 동작이 끝난 이후에 실행이 됩니다.
따라서 var는 for문을 거쳐 5가 된 상태로 setTimeout이 5번 실행되기 때문에 undefined가 5번 발생하게 됩니다.
이를 해결하기 위해선 var를 let으로 바꿔주어, 매 실행 시 마다 i값이 재생성되고, 해당하는 값을 setTimeout이 기억하고 있어(클로저) 올바른 값을 출력하도록 할 수 있습니다.
- console.log()에서 i는 5로 다섯 번, numbers[i]는 undefined를 다섯 번 리턴한다.
- setTimeout이 비동기함수로, for문이 모두 실행된 이후에 차례대로 다섯 번 실행되기 때문에
방지하기 위한 방법
- 즉시실행함수로 콜백함수를 감싸준다.
for (var i = 0; i < numbers.length; i++) { (function (i) { setTimeout(function () { console.log(i, numbers[i], new Date()); }, i + 1000); })(i); }
- var 대신 let 사용하기
- let이 block 스코프를 가지기 때문에, for문의 블럭 안에서 변수가 새로 생성이되고 이를 참조하게 된다.
- for대신 forEach 사용하기
- forEach도 각 배열 요소마다 함수를 새로 만들어 실행시키기 때문
numbers.forEach((number, i) => { setTimeout(function () { console.log(i, number, new Date()); }, i + 1000); });
1-6. var, let , const의 차이는 무엇인가요?
var VS let, const
- var - 함수스코프 (변수 재할당가능)
- 호이스팅 현상으로, 네임스페이스 오염되기 때문에 사용 지양
- let - 블록스코프 (변수재할당 가능)
TDZ
- 함수스코프 최상단과 실제 변수 선언이 이루어지는 곳 사이의 공간- TDZ에서 변수 사용하지 말 것
- const - 블록스코프 (변수재할당 불가능)
호이스팅
- 변수와 함수가 function scope의 최상단으로 선언이 끌어올려지는 현상
1-7. 클로저는 무엇인가요?
나의답변
클로저는 외부함수의 변수에 접근할 수 있는 내부함수, 혹은 그러한 동작을 의미합니다.
즉 외부함수에서 리턴된 내부함수가, 선언되었던 외부함수의 컨텍스트의 환경을 기억하여 외부함수의 변수에 접근할 수 있는 것을 의미합니다.
- 클로저의 private 효과
- private한 상태를 저장할 수 있다. (IIFE감싼 것과 같은 원리)
- 외부에서 해당 변수 접근 불가
- 오직 리턴된 해당 변수를 접근할 수 있는 내부함수를 통해 변경가능
- 클로저 사용패턴은?
- 커링 ,.. .