[DAY-9] VanillaJS를 통한 JS 기본 역량 강화(1)
😉 헷갈렸던 & 몰랐던 부분들만 정리하는 나만의 TIL
😯 모든 강의 내용은 적지 않아요!
오늘의 소감은?
코드의 잘못된 부분을 찾고 어떻게 해결할지 적으라는 문제 외에는 쉬웠습니다.
서술형 1번이었나..?
클로져의 개념에 대해서 한번 더 복습하는 좋은 시간이었습니다.
자..이제 선택 강의를 들으러 가 볼까요..
[1] JS 사전 문제 해설
한번 더 짚고 넘어가야 할 내용 위주로 정리해볼게요!
[1-1] 1번
function Cat(name, age) {
this.name = name;
this.age = age;
}
const haeyum = Cat("haeyum", 5);
console.log(haeyum.name);
이 코드를 보자마자 아 new
가 빠졌다를 알 수 있었습니다.
정확한 원리는 함수가 new
가 없이 실행이 되면, 위의 함수의 this
는 window
를 가리키게 됩니다.
따라서 오류가 뜸을 알 수 있습니다.
[1-2] 2번
(function (name) {
console.log(`hi ${name}`);
})("Jiyoung");
누가 봐도 즉시 실행함수이며, 선언 함과 동시에 실행됩니다. 따라서 hi Jiyoung
이 출력됩니다.
[1-3] 3번
var idiots = {
name: "idiots",
genre: "jazz",
members: {
Jiyoung: {
memberName: "Jiyoung",
play: function () {
console.log(`band ${this.name} ${this.memberName} play start`);
},
},
},
};
idiots.members.Jiyoung.play();
일단 idiots
의 members
객체에 접근 후 Jiyoung
객체를 기준으로 봐야 합니다.
play() 함수 내 this.name
은 Jiyoung
내 정의되어 있지 않으니, undefined
를 반환합니다.
따라서 band undefined Jiyoung play start가 출력됩니다.
[1-4] 4번
이 문제를 풀지 못했습니다!!!!!!
왜 틀렸는지는 알았지만, 어떻게 고쳐야할지 감이 오지 않았습니다.
function Band(members) {
this.members = members;
this.perform = function () {
setTimeout(function () {
this.members.forEach(function (member) {
member.perform();
});
}, 1000); // 1000은 1초
};
}
var theOralCigarettes = new Band([
{
name: "Jiyoung",
perform: function () {
console.log("Sing: a e u i a e u i");
},
},
]);
theOralCigarettes.perform();
setTimeout ~~ ,1000
⇒ 1초 뒤에 ~~ 를 실행해라
- 오류 발생 원인
JS에서는 항상 function scope에 유의를 해야합니다.
setTimeout
안에 있는 function의 this
는 Band의 this
를 가리키지 않습니다.
setTimeout
안에서 정의된 function 내 scope를 가리키게 됩니다.
따라서 this.members
는 undefined
이므로 오류가 발생하게 됩니다.
그럼 어떻게 고칠 수 있을까요?
- 해결 방법
(1) arrow function
화살표 함수는 화살표 함수 자체로 function scope를 만들지 않습니다.
해당 화살표 함수의 상위 함수의 scope를 가지게 됩니다.
따라서 this
가 Band의 this
를 가리키게 됩니다.
function Band(members) {
this.members = members;
this.perform = function () {
setTimeout(() => {
this.members.forEach(function (member) {
member.perform();
});
}, 1000);
};
}
(2) bind 사용
bind는 쉽게 말해 함수를 만드는 함수입니다.
bind를 붙인 곳은 setTimeout
내 function 밖이기 때문에, Band의 this
에 접근할 수 있습니다.
따라서 bind는 Band의 this를 setTimeout
내 function에 넘겨주는 함수를 생성하게 됩니다.
function Band(members) {
this.members = members;
this.perform = function () {
setTimeout(
function () {
this.members.forEach(function (member) {
member.perform();
});
}.bind(this),
1000
);
};
}
(3) 클로저 사용
Band의 this
를 that
의 변수에 넣었습니다.
따라서 setTimeout
내 function에서 that
에 접근하여 this
를 사용할 수 있습니다.
function Band(members) {
var that = this;
this.members = members;
this.perform = function () {
setTimeout(function () {
that.members.forEach(function (member) {
member.perform();
});
}, 1000);
};
}
[1-5] 5번
const nums = [0, 1, 2, 3, 4];
for (var i = 0; i < nums.length; i += 1) {
setTimeout(function () {
console.log(`[${i}] number ${nums[i]} turn!`);
}, i * 1000);
}
이미 i는 다 증가해 5이므로 [5] number undefined turn!이 5번 뜨게 됩니다.
- 해결 방법
(1) IIFE (즉시 실행 함수)
i가 0, 1, 2, 3, 4일 때를 각각의 function scope로 가두어서 처리합니다.
따라서 setTimeout
실행 시점에 참고하는 idx
는 IIFE에서 인자로 넘긴 i값을 쓰게 됩니다.
for (var i = 0; i < nums.length; i += 1) {
(function (idx) {
setTimeout(function () {
console.log(`[${idx}] number ${nums[idx]} turn!`);
}, i * 1000);
})(i);
}
(2) var 대신 let 사용
let으로 선언할 경우, setTimeout
내에서 let i가 0일때, 1일때 각각 참조됩니다.
즉 각각의 scope가 생성된다는 것이 IIFE와 유사하게 동작합니다.
for (let i = 0; i < nums.length; i += 1) {
setTimeout(function () {
console.log(`[${i}] number ${nums[i]} turn!`);
}, i * 1000);
}
(3) forEach 사용
forEach같은 경우는 배열 요소를 하나씩 꺼내서 반복하는 함수입니다.
forEach로 nums를 순회하면서 각각의 function을 만들기 때문에 idx
의 값이 고유해집니다.
nums.forEach(function (num, idx) {
setTimeout(() => {
console.log(`[${idx}] number ${num} turn!`);
}, idx * 1000);
});
[1-6] var, let, const
- var는 function scope - 실행 시 맨 위로 var 선언이 끌어올려지기 때문에 호이스팅 발생
-
let, const는 block scope
- var와 let은 변수 재할당이 가능합니다.
- const는 변수 재할당이 불가능합니다.
[1-7] 클로져
outterFunc()
를 잘 살펴봅시다.
outterFunc()
가 실행 후, name 변수는 소멸해야 하나 console.log(name);
이 잘 동작합니다.
function outterFunc() {
const name = "Jiyoung";
return function () {
console.log(name); // function 외부에 있는 name을 참고하고 있습니다.
};
}
const logName = outterFunc(); // logName은 클로져!
logName(); // Jiyoung
이렇게 만들어진 것을 클로져라고 합니다.
클로져는 함수, 함수가 선언된 어휘적 환경의 조합이라고도 합니다.
클로져로 private 효과를 줄 수 있습니다.
function outterFunc() {
const name = "Jiyoung";
let count = 0;
function plus() {
count += 1;
// 각각의 함수 내부에서는 외부의 값인 count에 접근하고 있습니다.
}
function minus() {
count -= 1;
}
function printName() {
console.log(`name : ${name}`);
}
function printCount() {
console.log(`count : ${count}`);
}
return {
plus,
minus,
printName,
printCount,
};
}
const outter = outterFunc();
outter.plus();
outter.plus();
outter.printCount(); // 2
outter.minus();
outter.printCount(); // 1
outter.printName(); // Jiyoung
console.log(outterFunc.count); // undefined
해당 함수를 보면, return한 함수에는 접근하여 count를 조작할 수 있지만 count에는 직접 접근하지 못합니다.
댓글남기기