TIL

함수 scope와 변수의 종류(var, let, const)

rachel_13 2022. 7. 18. 22:15

함수 스코프

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로 선언한 변수는 블록스코프의 영향을 받지 않는다.

👇 varlet 으로 바꾸기

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