함수 스코프
Scope : 범위, 유효공간, 유효 범위
- 함수 스코프 : 함수에 의해서 생기는 범위. 변수의 유효범위.
- 블록 스코프 : Block Scope : 블락에 의해 생기는 유효범위
{ }에 의해서 변수의 유효범위가 결정된다.
변수 선언 방식 종류
var
, let
, const
✔ 예제 1번
{
let a = 10; {
let a = 20;
console.log(a) //20
}
console.log(a) //10
}
console.log(a) //reference error
✔ 예제 2번
function hasValue(p) {
console.log(v) //undefined
if (p) {
//truthy
var v = 'blue';
console.log(v); //blue
} else {
//falsy
var v = 'red';
console.log(v);
}
console.log(v); //blue
}
hasValue(10);
//var로 선언한 변수는 블록스코프의 영향을 받지 않는다.
👇 var
→ let
으로 바꾸기
function hasValue(p) {
console.log(v) //v is not defined
if (p) {
//truthy
let v = 'blue';
console.log(v);
} else {
//falsy
let v = 'red';
console.log(v);
}
console.log(v);//v is not defined
}
hasValue2(10);
💡 블록스코프는 호이스팅을 할까?
블록스코프는 중괄호 {}의 영향을 받는다고 했다.
[참고사항]
data의 종류 : 문단, 식, 값*
(1) if문, for문, while문, switch-case문 => '문단'
결과를 리턴하지 않는다. 실행하고 끝이다.
→ 문 자체가 하나의 "block scope"가 된다.
(2)'표현식', expression. 값이 될 수 있는 경우.
(3)'값'scope의 종류는 2가지
ㄴ 함수에 의한 스코프
ㄴ 블록에 의한 스코프
if (true) {
//if문 block scope open1
let a = 10;
if (true) {
//if문 block scope open2
console.log(a); //✅a is not defined
const a = 20;
}
console.log(a); //10
}
console.log(a); //a is not defined
✅ 이 부분에서 호이스팅이 되는지의 여부를 판단하자
호이스팅이 된다면a
: undefined
호이스팅이 안된다면a
선언되지 않았으므로
상위 스코프로 이동해서 a가 선언된 곳을 찾는다.
→ a
= 10
라고 예상을 하고..! console창에 찍어보니
Reference Error가 뜬다!❌
TDZ
: Temporal Dead Zone (임시 사각 지대)
Ecmascript에서 정의한 개념은 아님
let, const에서 실제로 변수를 선언한 위치에 오기 전까지는 변수를 불러올 수 없다는 것!
💡 호이스팅
- 기존
var
:
1) 변수명만 위로 끌어올리고(호이스팅)
2) undefined를 할당한다.
(function () {
var a = 10;
(function () {
console.log(a); //undefined
var a = 20;
})();
console.log(a); //10
})();
console.log(a); //a is not defined
let
,const
1) 변수명만 위로 끌어올리고
(호이스팅을 하긴 한다!)
2) 끝!
👉undefined
할당이 없어서 참조 에러가 나는 것- 즉, 변수값이 매칭이 안되어 있다는 뜻*
if (true) {
let a = 10
if (true) {
console.log(a) //referecn Error: a is not defined.
const a = 20
}
console.log(a)
}
console.log(a);
this
var value = 0
var obj = {
value: 1,
setValue: function () {
this.value = 2; //this = obj → 따라서 obj.value = 2가 된다.
(function () {
this.value = 3
//this = window → 함수를 실행한 것 뿐, 메서드를 호출한 것이 아니므로, 함수 호출에 대한 this는 전역객체 window이다.
//→ window.value = 3;
// 전역변수 value =3;
})();
}
}
obj.setValue(); //메서드 호출
console.log(value); //3
console.log(obj.value) //2
💡 2개의 this를 일치시키고 싶다면?
var value = 0
var obj = {
value: 1,
setValue: function () {
var self = this
self.value = 2;
(function () {
self.value = 3
})()
}
}
obj.setValue();
console.log(value); //0
console.log(obj.value); //3
✔ call이나 apply써서 this를 불러와도 무방 → 동일한 결과가 나옴
var value = 0;
var obj = {
value: 1,
setValue: function(){
this.value = 2;
var a = function (){
this.value = 3;
}
a.call(this);
}
}
obj.setValue();
console.log(value); //0
console.log(obj.value); //3
ES6+버전에서는 간단하게 block scope
를 쓰면된다.
let value = 0
let obj = {
value: 1,
setValue: function () {
this.value = 2 {
this.value = 3
}
}
}
obj.setValue()
console.log(value)
console.log(obj.value)
block scope는 this에 영향을 받지 않는다.
모든 '문'의 형태에 적용
각 콘솔에 찍히는 값을 유추해 보자EX1
{
let a = 2
if (a > 1) {
let b = a * 3
console.log(b) //6
} else {
let b = a / 3
console.log(b)
}
// console.log(b) //Reference Error
}
console.log(a) //Reference Error
EX2
if (Math.random() < 0.5) {
let j = 0
console.log(j)
} else {
let j = 1
console.log(j)
}
console.log(j) // if..else문을 벗어나 있음 -> Reference Error! (j is not defined)
EX3
let a = Math.ceil(Math.random() * 3)
switch (a) {
case 1: {
let b = 10
console.log(a + b) //11
break
}
case 2: {
let b = 20
console.log(a + b) //22
break
}
case 3: {
let b = 30
console.log(a + b) //33
break
}
}
console.log(a, b) //b is not defined
EX4
var sum = 0
for (let i = 1; i <= 10; i++) {
sum += i
}
console.log(sum)
console.log(i) //i라는 변수는 for문 안에서만 유효함 따라서 Reference Error! i is not defined가 나옴
변수 let
let a = 1
function f() {
console.log(a, b, c) //b, c is not defined
let b = 2
console.log(a, b, c) //c is not defined
if (true) {
let c = 3
console.log(a, b, c) //1, 2, 3
}
console.log(a, b, c) //c is not defined
b
}
f()
👉let은 block scope
에 갇힌다, TDZ가 있다.(❗var 변수와의 차이점)
var funcs = []
for (var i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i);
})
}
funcs.forEach(function (f) {
f();
});
👉 결과 : 10이 열 번 나온다.
[
function(){console.log(i);},
function(){console.log(i);},
function(){console.log(i);},
function(){console.log(i);},
...
]
💡 실행을 언제 하는가?
if문이 다 돌고 나서 다음번 forEach 돌릴때 그제서야 실행된다.
▪ WHY? 실행 컨텍스트는 함수를 호출했을 때(실행할 때) 열리기 때문!
▪ 실행할 때 비로소 변수를 hoisting하고 this를 바인딩하고 자신한테 없는 변수를 외부에서 참조하려고 찾는다.
▪ i를 찾아야 하는데 외부 스코프 즉 전체 스코프 공간에 i는 for문에서 선언했던 i가 있다.
이 값은 10번 돌고 i=9일때까지 실행되고 난 후 i++에 의해 i=10
인 상태이다.
→ for문이 끝난 후에는 i가 10이 된 상태이다.
→ 배열에 10번을 돌아도 계속 값이 10
으로 찍히게 되는 것이다.
💡 그럼 원래 나오게 하고 싶었던대로 i가 0 ~9까지 출력되게 하려면 어떻게 해야 될까?
i를 살아있게 해줘야 한다.
각각의 for문 안에서 i를 { } 안에 넘겨줘야 한다. => 클로저 이용! (ES5)
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function (v) {
return function () {
console.log(v)
}
})(i));
}
funcs.forEach(function (f) {
f();
})
ES6에서는 어차피 for문이 block scope이기 때문에 let
변수 사용하면 i 값이 별도의 처리를 하지 않아도 그냥 block scope 내에 존재하게 된다.
let funcs = []
for (let i = 0; i < 10; i++) {
funcs.push(function () {
console.log(i)
})
}
funcs.forEach(function (f) {
f()
})
변수 const
'TIL' 카테고리의 다른 글
돔(DOM) 1탄 (0) | 2022.07.18 |
---|---|
데이터베이스(Database) (0) | 2022.07.18 |
리눅스(Linux) & 터미널(Terminal) (0) | 2022.07.18 |
개발자 도구 (0) | 2022.07.18 |
모바일 웹 구현 (0) | 2022.07.18 |