개발일지

Java Script 언어의 특성( 객체와 불변성, 자료형, 스코프, 호이스팅과 TDZ...) 본문

JavaScript

Java Script 언어의 특성( 객체와 불변성, 자료형, 스코프, 호이스팅과 TDZ...)

MotherCarGasoline 2022. 5. 20. 16:20

목차

더보기

🐤 JavaScript의 자료형과 JavaScript만의 특성은 무엇일까 ?

  • 느슨한 타입(loosely typed)의 동적(dynamic) 언어
  • JavaScript 형변환
  • ==, ===
  • 느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고
    보완할 수 있는 방법에는 무엇이 있을지 생각해보세요.
  • undefined와 null의 미세한 차이들을 비교해보세요.

 🐤 JavaScript 객체와 불변성이란 ?

  • 기본형 데이터와 참조형 데이터
  • 불변 객체를 만드는 방법
  • 얕은 복사와 깊은 복사

🐤 호이스팅과 TDZ는 무엇일까 ?

  • 스코프, 호이스팅, TDZ
  • 함수 선언문과 함수 표현식에서 호이스팅 방식의 차이
  • 여러분이 많이 작성해온 let, const, var, function 이 어떤 원리로 실행되는지 알 수 있어요.
  • 실행 컨텍스트와 콜 스택
  • 스코프 체인, 변수 은닉화

🐤 실습 과제

 

JavaScript의 자료형과 JavaScript만의 특성은 무엇일까 ?

 

 

1. 느슨한 타입(loosely typed)의 동적(dynamic)언어

  먼저 느슨한 타입은 타입 없이 변수를 선언하는거고, 강력한 타입은 타입과 변수를 함께 선언해야만 한다

/* JavaScript Example (loose typing) */
var a = 13; // Number 선언
var b = "thirteen"; // String 선언

/* Java Example (strong typing) */
int a = 13; // int 선언
String b = "thirteen"; // String 선언

동적언어란?

 

  사용자가 주는 정보를 토대로 웹페이지가 유동적으로 변경할 수 있게 만들어주는 언어다. 동적언어인 javascript로 코드를 만들면 만든 코드에 따라 어떤 행동을 했을 때 경고문이 보여진다거나 버튼을 눌렀을 때 웹페이지를 어두워지게 만들어지는 등 사용자가 주는 정보에따라 웹페이지를 변화 시킬 수 있다. 하지만 사전에 버그를 미리 간파하기 어렵다.

 

2. JavaScript 형변환

 

- 문자형을 숫자형으로 변환하기

 

var 변수 = parseInt(문자);    //문자를 정수형 숫자로 변환해줌

var 변수 = parseFloat(문자);     //문자를 실수형 숫자로 변환해줌

var 변수 = Nember(문자);    //문자를 정수&실수형 숫자로 변환해줌

 

- 숫자형을 문자형으로 변환하기

 

var 변수 = String(숫자);    //숫자를 문자로 변환해줌

var 변수 = 숫자.toString(진법);    //숫자를 문자로 변환해줌 - 변환하면서 진법을 바꿀 수 있음

var 변수 = 숫자.toFixed(소수자리수);    //숫자를 문자로 변환해줌 - 실수형의 소수점 자리를 지정할 수 있음

var 변수 = 숫자 + "문자열";    //숫자와 문자열을 한 문자열로 더해줌

 

3. == , === 차이

 

값은 똑같이 1이지만 값의 종류가 숫자냐, 문자열이냐에 따라 === 연산자를 사용할 때 결과가 false라고 나온다.

var a = 1; 
var b = "1"; 
console.log(a == b); // true 
console.log(a === b); // false

 

4. 느슨한 타입(loosely typed)의 동적(dynamic) 언어의 문제점은 무엇이고 보완할 수 있는 방법에는 무엇이 있을지 생각해보세요.

 

실행 도중에 변수에 예상치 못한 자료형이 들어와 TypeError를 발생할 수 있음.

동적타입 언어는 런타임 시 확인할 수 밖에 없기 때문에, 코드가 길고 복잡해질 경우 타입 에러를 찾기가 어려워 집니다.

=> 동적 타입의 언어의 단점을 보완하기 위해 TypeScript가 등장했다. 타입스크립트는 변수를 선언할 때, 해당 변수의 타입을 함께 선언해주어 컴파일 단계에서 타입 오류를 파악할 수 있다. 

 

 

5. undefined와 null의 미세한 차이들을 비교해보세요.

 

  undefined 변수를 선언하고 값을 할당하지 않은 상태, null은 변수를 선언하고 빈 값을 할당한 상태(빈 객체)이다. 즉, undefined는 자료형이 없는 상태이다.

따라서 typeof를 통해 자료형을 확인해보면 null은 object로, undefined는 undefined가 출력되는 것을 확인할 수 있다.

let a =      // undefined
let b = '';  // null

typeof null // 'object'
typeof undefined // 'undefined'

null과 undefined는 공통적으로 값이 없음을 뜻하지만, 값의 종류(Data Type)가 다르기 때문에, 

=== 연산자를 사용할 때 결과가 false라고 나온다. 

console.log(null == undefined); // true 
console.log(null === undefined); // false

 

JavaScript 객체와 불변성이란?

 

1.기본형 데이터와 참조형 데이터

 

기본형(,Primitive Type)은 값을 그대로 할당한다 참조형(Reference Type)은 값이 저장된 주소값을 할당(참조)한다.

 

2. 불변 객체를 만드는 방법

 

  참조형 데이터는 기본형 데이터와 마찬가지로 데이터 자체를 변경하려고 한다면 데이터는 변하지 않습니다.

하지만 참조형 데이터가 가변적이다라고 말하는 것은, 내부 프로퍼티를 변경할 때를 말합니다.

만약 객체를 복사해서, 내부 프로퍼티를 변경하고 싶을 때, 복사한 객체를 변경하더라도, 원본 객체가 변하지 않아야 하는 경우가 생길 것입니다. 이런 경우에 '불변 객체'가 필요합니다. 불변 객체를 만들기 위해서는 다양한 방법을 활용할 수 있습니다.

 

2 - 1const

  자바스크립트 키워드 중 하나인 const이다. ES6문법부터 let과 const를 지원한다.

const 키워드는 변수를 상수로 선언할 수 있다, 일반적으로 상수로 선언된 변수는 값을 바꾸지 못하는 것으로 알려져 있다.

const test = {};
test.name = "mingyo";

console.log(test);  // {"mingyo"}

  ES6에서의 const는 할당된 값이 상수가 되는 것이 아닌 바인딩된 값이 상수가 되는, 즉 test변수가 상수가 되기 때문에 const 키워드로 선언된 test변수에는 객체 재할당은 불가능하지만 객체의 속성은 변경 가능하다.

재할당이 불가능 한 이유는 변수와 값(객체) 사이의 바인딩 자체가 변경이 되기 때문에 상수인 test변수는 재할당이 불가능한 것이고

객체의 속성이 변경가능 한 이유는 실제 객체가 변경은 되지만 ( {} -> name : "mingyo" ) 객체와 변수(test)사이의 바인딩은 변경이 되지 않기 때문에 객체의 속성은 변경가능한 것이다. 

때문에 비록 재할당은 불가능하지만 객체의 속성을 변경함으로 인해 변수에 바인딩된 객체의 내용까지 변경이 되기 때문에 불변객체라고 하기는 힘들다. 따라서 Object.freeze()라는 JS내장메소드도 살펴보도록 하겠다.

 

2- 2 Object.freeze()

  자바스크립트에서 기본적으로 제공하는 메소드인 Object.freeze() 메소드이다. 공식 문서에서는 "객체를 동결하기 위한 메소드" 

라고 적혀있다.

let test = {
    name : 'kim'
}

Object.freeze(test);

사용법은 간단하다. test 변수에 key value를 가진 객체를 바인딩 후 Object.freeze(test)를 사용해 바인딩된 변수를 동결 객체로 만들었다. 때문에 test 객체는 객체의 속성을 변경하는 시도는 불가능하다.

test.name = 'Jung';
console.log(test) // {name: 'kim'}

위와 같이 객체의 속성을 변경하는 시도는 무시된다.

 

 

3. 얕은 복사와 깊은 복사

 

3 - 1 얕은 복사 (Shallow Copy)

 

객체를 복사할 때, 객체가 가지고 있는 필드의 값들을 단순히 복사한다. 기본형(Primitive Type) 값들은 복사가 될 것이고,

참조형 변수는 같은 객체를 가리키게 된다.

const obj1 = { a: 1, b: 2};
const obj2 = obj1;
console.log( obj1 === obj2 ); // true

const obj1 = { a:1, b:2 };
const obj2 = obj1;
obj2.a = 100;
console.log( obj1.a ); // 100

  위의 예시처럼 객체를 직접 대입하는 경우 참조에 의한 할당이 이루어지므로 둘은 같은 데이터(주소)를 가지고 있다.

 

 

3 - 2 깊은 복사 (Deep Copy)

 

객체를 복사할 때, 객체가 가지고 있는 필드의 값을 복사해준다. 이 때, 기본형 값들은 그대로 복사가 되며 참조형 변수의 경우 변수가 참조하는 객체에 대해서도 새롭게 복사해서 만들어준다.

 

얕은 복사 / 깊은 복사 https://hbase.tistory.com/91

우선 얕은복사에서 객체가 복사될 때, 배열에 대한 참조값만 복사가 된다. 메모리 상에 배열은 하나만 존재하고 이 배열에 대한 주소값만 복사가 되기 때문에 원본 object 변수를 통해서 수정한 값이 copiedObject 에도 적용되는 것이다.

 

  깊은복사에서는 객체가 참조하고 있는 또 다른 객체인 배열까지 복사한 다음 그 안쪽에 있는 내용을 모두 복사하기 때문에 원본 object 변수를 통해서 수정한 값이 copiedObject 변수에서 참조하는 값에 영향을 미치지 않는다.

부작용이 없는 코드를 작성하려면 이 두 가지 복사에 대해서 염두에 두어야 한다. 일반적으로 깊은복사(Deep Copy)는 데이터들을

안전하게 처리할 수 있다. 다른 곳에서의 참조가 객체를 수정할 염려가 없기 때문이다.

 

  반대로 얕은복사(Shallow Copy)는 빠르다. 객체의 참조를 따라가면서 복사하지 않기 때문이다. 만약 참조하는 객체가 불변(Immutable)의 성질을 가지고 있는 객체라면 깊은복사를 할 이유는 없다. 즉, 한번 만들어지고 수정이 되지 않는 형태라면, 얕은복사를 통해 메모리 사용량도 줄일 수 있고, 빠른 성능도 얻을 수 있게 된다.

 

 

호이스팅과 TDZ는 무엇일까 ?

 

1. 스코프, 호이스팅, TDZ

1 - 1 스코프(Scope)?

‘범위’라는 뜻. 즉, 스코프(Scope)란 ‘변수에 접근할 수 있는 범위’라고 할 수 있는데요. 자바스크립트에선 스코프는 2가지 타입이 있습니다. 바로  global(전역)과 local(지역) 

var a = 1; // 전역 스코프  = 함수 밖에서 선언 프로그램 전체에서 접근 가능
function print() { // 지역(함수) 스코프 = 함수 안에서 선언되어 함수 내부에서만 접근 가능
 var a = 111;
 console.log(a);
}
print();
console.log(a);

 

1 - 2 호이스팅(Hoisting)?

 

  함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것.

var는 함수 스코프. 함수 내에서 선언되면 함수 밖에서 사용할 수 없다. let, const는 블록 스코프. 모든 코드 블록에서 선언된 변수는 코드 블록 내에서만 유효하며 외부에서는 접근할 수 없다.

자바스크립트에서는 아래와같이 작성해도 에러가 나지 않는다.

console.log(name);   // undefined
var name = "홍길동";

자바스크립트에서 선언과 할당을 나중에 해주었는데도 오류가 나지 않는 이유는 호이스팅(Hoisting)때문이다.

실제로 동작하는 모습은 다음과 같다.

var name;
console.log(name);
name = "홍길동";

변수의 선언 부분이 맨 위로 끌어올려지는 것을 호이스팅이라고 한다. undefined가 출력되는 이유는 변수의 선언만 올려지고 할당은 올려지지 않기 때문이다. 즉, 선언은 호이스팅 되지만 할당은 호이스팅 되지 않는다.

그러나 let과 const의 경우 에러가 발생한다.

console.log(a);
const a = "AAA";
console.log(a);

var와는 다른 결과를 내는 이유는 let과 const가 TDZ의 영향을 받기 때문이다. TDZ (Temporal Dead Zone)는 변수 선언 이전에 변수를 참조하는 영역이다. 해당 영역에서 선언 이전에 참조한 변수는 참조 에러가 발생한다. 위의 코드의 경우 첫번째 줄이 TDZ에 해당한다.

 

var는 선언과 동시에 undefined로 초기화가 이루어진다. let은 선언과 초기화가 분리되어 진행된다. 호이스팅되면서 선언단계가 이루어지지만 초기화 단계는 실제코드에 도달했을 때 이루어지기 때문에 ReferenceError가 발생한다. const는 선언과 할당을 같이 해주어야한다.

이러한 이유로 var는 에러가 발생하지 않지만 let과 const는 에러를 발생시킨다.

 

 

1 - 3 TDZ(Temporal Dead Zone)?

 

  TDZ 란 우리 말로 번역하면 일시적 사각지대라는 의미이며, 스코프 시작 ~ 초기화 시작 사이의 구간을 의미합니다.

다른 말로 변수가 선언되고 변수의 초기화가 이루어지기 전까지의 구간이라고 말할 수 있겠습니다. 그림으로 표현하면 다음과 같이 표현 할 수 있을 것 같습니다.

https://funveloper.tistory.com/25

 변수 생성 단계

 

1. 선언 단계 (Declaration phase)

변수를 실행 컨텍스트의 변수 객체에 등록하는 단계

 

2. 초기화 단계 (Initialization phase)

실행 컨텍스트에 등록한 변수를 위한 메모리를 만드는 단계, 메모리가 만들어지면 처음에는 undefined 가 할당

 

3. 할당 단계 (Assignment phase)

사용자가 undefined 로 할당된 변수에 다른 값을 할당하는 단계

 

* var 는 선언과 초기화 단계가 동시에 이루어집니다.

* let, const 는 선언, 초기화, 할당 단계가 각각 따로 이루어집니다.

* function (함수 선언문) 은 선언, 초기화, 할당 단계가 동시에 이루어집니다.

 

그렇기 때문에 선언과 초기화 단계가 따로 이루어 지는 let, const 같은 경우는 TDZ에 영향을 받을 수 밖에 없습니다.

 

TDZ 예시

function function1() {
  console.log('너의 이름은?', uname);
  var uname = '홍길동';
}

=> function1 함수 스코프 안에서 uname 변수가 선언되기 전에 참조를 하였기 때문에 TDZ 에서 에러가 발생해야 하지만, var 로 선언한 변수가 호이스팅(선언, 초기화 진행) 되어 에러가 발생하지 않고 undefined 가 표시됨

 

 

2. 함수 선언문과 함수 표현식에서 호이스팅 방식의 차이

함수선언문 (Function Declaration)
일반적인 프로그래밍 언어에서의 함수 선언과 비슷한 형식

function 함수명() {
  구현 로직
}

// 예시
function funcDeclarations() {
  return 'A function declaration';
}
funcDeclarations(); // 'A function declaration'

https://joshua1988.github.io/web-development/javascript/function-expressions-vs-declarations/


함수표현식 (Function Expression)


변수값에 함수 표현을 담아 놓은 형태

 - 유연한 자바스크립트 언어의 특징을 활용한 선언 방식
함수표현식은 익명 함수표현식과 기명 함수표현식으로 나눌 수 있다.
 - 일반적으로 함수표현식이라고 부르면 앞에 익명이 생략된 형태라고 볼 수 있다.
 - 익명 함수표현식: 함수에 식별자가 주어지지 않는다.
 - 기명 함수표현식: 함수의 식별자가 존재한다.
함수표현식의 장점
 - 클로져로 사용
 - 콜백으로 사용(다른 함수의 인자로 넘길 수 있음)

var test1 = function() { // (익명) 함수표현식
  return '익명 함수표현식';
}

var test2 = function test2() { // 기명 함수표현식 
  return '기명 함수표현식';
}
https://gmlwjd9405.github.io/2019/04/20/function-declaration-vs-function-expression.html
var 함수명 = function () {
  구현 로직
};

// 예시
var funcExpression = function () {
    return 'A function expression';
}
funcExpression(); // 'A function expression'

 

 

함수선언문과 함수표현식의 차이
함수선언문은 호이스팅에 영향을 받지만, 함수표현식은 호이스팅에 영향을 받지 않는다.

함수선언문은 코드를 구현한 위치와 관계없이 자바스크립트의 특징인 호이스팅에 따라 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어 올려진다.
함수표현식은 함수선언문과 달리 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있다.

 

ex ) 함수표현식 Error

/* 정상 */
function printName(firstname) { // 함수선언문
    var inner = function() { // 함수표현식
        return "inner value";
    }

    var result = inner();
    console.log("name is " + result);
}

printName(); // "name is inner value"
https://gmlwjd9405.github.io/2019/04/20/function-declaration-vs-function-expression.html
/* 오류 */
function printName(firstname) { // 함수선언문
    console.log(inner); // "undefined": 선언은 되어 있지만 값이 할당되어있지 않은 경우
    var result = inner(); // ERROR!!
    console.log("name is " + result);

    var inner = function() { // 함수표현식 
        return "inner value";
    }
}
printName(); // TypeError: inner is not a function

/** --- JS Parser 내부의 호이스팅(Hoisting)의 결과 --- */
/* 오류 */
function printName(firstname) { // 함수선언문
    var inner; // Hoisting - 변수값을 끌어올린다. (선언은 되어 있지만 값이 할당되어있지 않은 경우)
    console.log(inner); // "undefined"
    var result = inner(); // ERROR!!
    console.log("name is " + result);

    inner = function() { // 함수표현식 
        return "inner value";
    }
}
printName(); // TypeError: inner is not a function
https://gmlwjd9405.github.io/2019/04/20/function-declaration-vs-function-expression.html

Q. “printName이 is not defined” 이라고 오류가 나오지 않고, function이 아니라는 TypeError가 나오는 이유?
A. printName이 실행되는 순간 (Hoisting에 의해) inner는 ‘undefined’으로 지정되기 때문
inner가 undefined라는 것은 즉, 아직은 함수로 인식이 되지 않고 있다는 것을 의미한다.
함수표현식보다 함수선언문을 더 자주 사용하지만, 어떤 코딩컨벤션에서는 함수표현식을 권장하기도 한다.
즉, 어떤 컨벤션을 갖던지 한가지만 정해서 사용하는 게 좋다.

 

3. 여러분이 많이 작성해온 let, const, var, function 이 어떤 원리로 실행되는지 알 수 있어요.

var 변수는 재선언이 가능하다.

var name = "joojaewoo";
console.log(name); // joojaewoo

var name = "joo";
console.log(name); // joo

변수를 한 번 더 선언했음에도 불구하고, 서로 다른값이 잘 출력된다. 유연하게 사용될 수도 있지만, 코드량이 많아진다면 어디서 선언되었고 어떻게 사용되는지 파악하기 힘들 뿐 아니라 값이 바뀔수도 있다.

 

let와 const는 ES6이후 var을 보완하기 위해 추가된 변수 선언방식이다.

let name = "joojaewoo";
console.log(name); // joojaewoo

let name = "joo";
console.log(name); // Uncaught SyntaxError: Identifier 'name' has already been declared

name이 이미 선언되었다는 에러가 발생한다. let와 const는 변수 재 선언이 불가능한 공통점을 가지고 있지만 둘의 차이점은 immutable 여부이다. let는 아래와 같이 변수에 재 할당이 가능하다.

let name = "joojaewoo";
console.log(name); //joojaewoo

name = "joo";
console.log(name); //joo

 

그러나 const는 재선언, 재할당이 모두 불가능하다.

const name = "joojaewoo";
console.log(name); // joojaewoo

name = "joo"; // Uncaught TypeError: Assignment to constant variable.
console.log(name);

 

4. 실행 컨텍스트와 콜 스택

 

실행 컨텍스트(Execution Context)

  자바스크립트의 핵심 개념으로, 코드를 실행하기 위해 필요한 환경이다.
더 자세히 말하자면, 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.
자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는 개념.
모든 코드는 특정한 실행 컨텍스트 안에서 실행된다.
javascript는 어떤 execution context가 활성화되는 시점에 선언된 변수들을 위로 끌어올리고(hoisting), 외부 환경 정보를 구성하고, this값을 설정하는 등의 동작을 수행하는데, 이로 인해 다른 언어에서는 발생할 수 없는 특이한 현상들이 발생한다.

자바스크립트의 주요한 실행 컨텍스트에는 두 가지가 있다.

 

-  글로벌 실행 컨텍스트 (Global Execution Context)

디폴트 실행 컨텍스트로, 자바스크립트 파일이 엔진에 의해 처음 로드되었을 때 실행되기 시작하는 환경이다.

무조건 하나의 전역 실행 컨텍스트 만이 존재하며, 애플리케이션이 종료될 때(웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지하는 것이다.

 

- 함수 실행 컨텍스트 (Fuction Execution Context)

우리가 execution context를 따로 구성하는 방법은 함수를 실행하는 것 뿐이다. 함수가 호출되고 실행됨에 따라서 해당 함수 안에서 생성되는 컨텍스트. 각각의 함수는 고유의 실행 컨텍스트를 가진다. 그리고 전역 실행 컨텍스트에 언제나 접근할 수 있다. 전역 실행 컨텍스트가 생성된 후, 함수가 실행(ex 호출) 될 때마다 새로운 실행 컨텍스트가 작성된다.

 

콜스택(Call Stack)

call은 호출을 뜻한다.
stack은 출입구가 하나뿐인 깊은 우물 같은 데이터 구조다.

따라서 callstack은 자바스크립트가 함수 호출을 기록하기 위해 사용하는 우물 형태의 데이터 구조이다.

항상 맨 위에 놓인 함수를 우선으로 실행된다. 이런 식으로 자바스크립트 엔진은 가장 위에 쌓여있는 context와 관련 있는 코드들을 실행하는 식으로 전체 코드의 환경과 순서를 보장한다.

 

5.  스코프 체인, 변수 은닉화

스코프 체인(scope chain)이란?

스코프 체인(Scope Chain)은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.

하지만 스코프 체인(scope chain)을 이해하기 위해서 먼저 자바스크립트의 실행 컨텍스트(Execution context)를 알아야 한다.

 

변수 은닉화

 

https://yeonee3219.tistory.com/174

이러한 방식과 같이 직접적으로 변경되면 안 되는 변수에 대한 접근을 막는 것을 은닉화라고 합니다.

 

- 객체지향 프로그래밍에서의 캡슐화란 ?

비슷한, 관련된 역할을 하는 속성과 메소드들을 하나의 틀(클래스 등) 안에 담는 것을 의미합니다.

 

- 캡슐화를 왜 할까요?

결론부터 말씀드리면 가장 중요한 이유는 정보은닉 때문입니다.

정보은닉이란, 객체에 대한 구체적인 정보를 노출시키지 않도록 하기 위한 기법입니다.

 

- 예를들면?

사원의 속성 중에 "월급" 이라는 속성이 있다고 합시다.

만약 Public으로 누구나 "월급"이라는 속성에 접근할 수 있다면,

사원.월급 = -500000 와 같은 현실세계에선 불가능한 코드를 작성할 수 있게 되죠.

 

그러나, 캡슐화를 해서 월급이라는 속성을 직접적으로 접근을 못하게 하고,

접근하는 방법을  get월급, set월급 이라는 메소드를 통해 접근하게 된다면

set월급(-500000) 와 같은 코드는 set월급 메소드 구현부에서 미연에 값을 검증할 수 있겠죠? 

 

실습과제

 

  • 콘솔에 찍힐 b 값을 예상해보고, 어디에서 선언된 “b”가 몇번째 라인에서 호출한 console.log에 찍혔는지,
    왜 그런지 설명해보세요. 주석을 풀어보고 오류가 난다면 왜 오류가 나는 지 설명하고 오류를 수정해보세요.

 

let b = 1;                        // 첫 번째 출력

function hi () {

const a = 1;

let b = 100;

b++;               // b + 1

console.log(a,b);  // 1, 101      
 
}                     

//console.log(a);                 // 오류 : 변수 a는 지역변수라 함수 밖에서는 호출 되지 않으며
                                  // a라는 값이 없다고 나온다 = a is not defined

console.log(b);                   // 첫 번째 출력 = 함수선언문 밖에 위치해서 b++;이 적용 안됌

hi();                             // 두 번째 출력 = 함수 선언식

console.log(b);                   // 세 번째 출력 = 첫 번째와 동일 글로벌 변수, 함수 밖에서 호출
                                  // 순서대로 출력

 

Comments