ES6+ 향상된 함수 기능 (1) - 매개변수


ES6에서 함수의 기능을 온전하게 완성했다고 볼 수 있다.

 - 매개변수에 기본값을 줄 수 있게 됨.

 - 나머지 매개변수를 통해 가변 길이 매개변수를 좀 더 명시적으로 표현할 수 있음.

 - Named parameter(명명된 매개변수)를 통해서 함수를 호출하는 코드의 가독성이 훨씬 좋아졌다.

 - Arrow function(화살표 함수)가 추가되면서 함수 코드가 간결해졌고, this 바인딩에 대한 고민을 덜 수 있게 되었다.


1.  매개변수에 추가된 기능.


#1 매개변수 기본값


ES6부터 함수 매개변수에 기본값을 줄 수 있다.


function printLog( a = 1 ) {
console.log({ a });
}
printLog(); // { a: 1 };



또한 객체 분해 할당처럼 기본값으로 함수 호출을 넣을 수 있고, 기본값이 필요한 경우에만 함수가 호출된다.


function getDefault() {
return 'None';
}
function printText(str = getDefault()) {
console.log({ str });
}
printText(); // { str: 'None'}



undefined가 입력된 경우에만 기본값 함수가 호출된다는 특징을 이용하면 매개변수에서 필숫값을 표현할 수 있다.


function required() {
throw new Error('No parameter');
}
function printText(str = required()){
console.log({ str });
}
printText('Hello'); // { str: 'Hello' }
printText(); // 에러 발생: No parameter


#2 Rest parameter (나머지 매개변수)


함수에 입력된 인수 중에서 정의된 매개변수 개수만큼을 제외한 나머지를 배열로 만들어 준다.

이는 변수가 가변적일 때 매우 유용하다.


function printText(str1, ...rest){
console.log({ str1, rest });
}
printText('A','B','O'); //{str1:'A', rest:['B','O']}

첫 번째 인자를 제외한 나머지를 rest 변수에 할당한다.


ES5에서는 arguments 키워드가 비슷한 역할을 한다.


function printText(str) {
const rest = Array.from(arguments).slice(1);
console.log({str, rest});
}
printText('a','b',1, 2);
// { str: 'a', rest: ['b',1,2] }

arguments는 명시적으로 드러나지 않아 가독성이 좋지 않다. 

그리고 arguments는 배열이 아니여서 배열처럼 사용하기 위해서는 배열로 변환해야한다.

Rest parameter ( 나머지 매개변수)를 사용하는게 낫다.


#3 Named parameter (명명된 매개변수)


명명된 매개변수는 객체 분해 할당을 이용해서 구현할 수 있다. 

함수 호출 시 매개변수의 이름과 값을 동시에 적을 수 있으므로 가독성이 높다.


const numbers = [1,2,3,4,5,6,7,8];
// 1번 함수
function getValues1(numbers,greaterThan,lessThan){
return numbers.filter((x) => x<lessThan&&x>greaterThan);
}
getValues(numbers, 2, 7); // 1
getValues(numbers, null, 7); // 2

// 2번 함수
function getValues2({ numbers, greaterThan = 0,lessThan =10}) {
return numbers.filter((x) => x<lessThan&&x>greaterThan);
}

getValues2({numbers, greaterThan:2, lessThan:7}); // 3
getValues2({numbers, lessThan:7}); // 4
getValues2({numbers, greaterThan:2}); // 5

1번 함수는 매개변수의 순서를 지켜야하는 함수이다. 

1번 실행에서는 2와 7이 무엇을 의미하는지 파악하기 어렵다.

2번 실행에서는 파라미터의 순서와 역할을 정확히 파악하고 필요없는 파리미터를 null로 채웠다.


반면 2번 함수는 Object 를 사용해 파라미터를 편하게 받을수 있게 하였다.

기본값도 할당이 가능하다. 객체 분해 할당 기능이 적용된다.

3번 실행 처럼 매개변수 이름을 노출 시켜 가독성을 높여준다.

4,5번 실행과 같이 Optional parameter(선택적 매개변수) 활용도를 높여준다.


여기서 함수를 호출할 떄마다 객체가 생성되기 때문에 비효율적일 것이라고 생각할 수 있다.

하지만 javascript 엔진이 최적화를 통해 새로운 객체를 생성하지 않는다고 한다. 안심하고 사용할 수 있다.


Name parameter 는 패턴이라고 할수 있다. 

유용한 예를 하나 살펴보자.


function apiRequest({
endpoint,
method = 'GET',
getParams = {},
headers = ['Content-Type: text-plain'],
callback = () => {},
timeout = 0,
authRequest = true,
} = {}) {
//...
console.log(arguments[0]);
return new Promise((resolve, reject) => {
resolve("resolve");
});
}

apiRequest({
endpoint: 'products',
getParams: { category: 3 },
})
// {endpoint: "products", getParams: { category: 3 }}


외에도 apiRequest 함수의 특수 버전을 만들 수 있습니다. 

아래와 같이 권한이 필요한 authApiRequest를 만들어 보자.


function authApiRequest (params) {
const token = 'whatever';
return new Promise((resolve, reject) => {
apiRequest({
...params,
headers: params.headers,
Authentication: token
})
.then(resolve)
.catch(reject)
});
}
// 실행 예제
authApiRequest({
endpoint: 'products',
getParams: { category: 3 },
});


또는 postRequest


function postRequest (params) {
return new Promise((resolve, reject) => {
apiRequest({...params, method: 'POST'})
.then(resolve)
.catch(reject)
});
}
// 실행 예제
postRequest({
endpoint: 'products',
getParams: { category: 3 },
});


똑같은 Named parameter 패턴으로 여러 변수들을 반환하는 함수를 만들어 봅시다.


function apiRequest({
endpoint,
method = 'GET',
getParams = {},
headers = ['Content-Type: text-plain'],
callback = () => {},
timeout = 0,
authRequest = true,
} = {}) {
//...
console.log(arguments[0]);
var response, error, loading
response = error = loading = null
//some amazing code...
return {
response,
error,
loading,
}
}

var { response, error, loading } = apiRequest({ endpoint: 'products' });

이런 식으로 함수의 응답을 구조화하여 다시 전후 관계와 의도를 제공 할 수 있습니다.


이것은 효과적으로 "함수에서 둘 이상의 값을 반환"하는 좋은 방법입니다. 

여러분들은 이 함수가 Object를 반환하고 있지만 또 다른 의미를 이미 알고있습니다.

바로 React Hooks 처럼 배열을 사용하여 값을 저장하고 반환 할 수 있습니다.



import React, { useState } from 'react';

function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}


블로그 이미지

steadily

,

ES6+ 향상된 Object, Array (2)


2.  간편하게 객체와 배열 속성값 가져오기.


#1 Spread operator (전개 연산자)


객체의 모든 속성을 꺼내어 나열하여 사용할 때 사용하는 문법이다.


Math.max(2, 5, 10); // 1
const numArr = [1, 2, 5, 6, 9];
Math.max(...numArr); // 2

1번의 방식은 한번 작성하고 나면 동적으로 변경할 수 없다.

하지만 2번에서는 전개 연산자를 사용하여 얼마든지 동적으로 변경된 객체를 매개변수로 전달할 수 있다.


이는 객체나 배열을 복사할 때도 유용하다.


const array1 = [1,2,4];
const object1 = { gender: 'Male', name: 'Peter'};
// 원본 객체를 전개하여 새로운 객체 생성
const array2 = [...array1];
const object2 = { ...object1};
// 속성을 수정하여도 원본 객체에 영향을 주지 않는다.
array2.push(9);
object2.gender = 'Female';



* 배열 = 전개 연산자를 사용해도 그 순서는 유지된다.

console.log( [1, 2, ...[3,4,5], 6] ); // [1,2,3,4,5,6]
new Date( ...[2020,10,12] );
// Thu Nov 12 2020 00:00:00 GMT+0900 (Korean Standard Time)

Date 함수처럼 정의된 매개변수를 순서대로 입력해야 할 때 유용하게 사용할 수 있다.


전개 연산자를 사용하여 서로 다른 두 배열 또는 두 객체를 쉽게 합칠 수 있다.


const object1 = { gender: 'Male', name: 'Peter'};
const object2 = { age: 21 };
const object3 = { ...object1, ...object2};
console.log(object3); // {gender: "male", name: "peter", age: 21}


이때 객체 리터럴에서 속성명이 중복된다면...

ES5까지는 에러가 발생했으나 ES6부터 허용된다.


const object1 = { a: 1, a: 3, b: 4, c: 5};
// { a: 3, b: 4, c: 5 }
const object2 = { ...object1, c: 7 };
// { a: 3, b: 4, c: 7 }

중복 시 최종 결과는 마지막 속성 명의 값이 된다.

* 전개 연산자를 활용하면 원본 객체에 영향을 주지 않고, 특정 속성값 (c: 7)을 변경하고 새로운 객체를 만들어 낼 수 있다.

이는 수정 불가능 변수를 관리할 때 유용하게 사용된다.



#2 Array destructuring assignment(배열 분해 할당)


배열의 여러 속성값을 변수로 분해하여 쉽게 할당할 수 있는 문법이다.

배열 분해 할당 예제를 살펴보자.


const arr = [1, 2];
const [a, b] = arr; // 배열의 속성값을 순서대로 할당한다.
console.log(a); // 1
console.log(b); // 2


다음과 같이 이미 정의된 변수에도 할당이 가능하다.


let a, b;
[a, b] = [1, 2];


초기 default 값 설정도 가능하다.

const arr = [99];
const [a = 1, b = 2] = arr;
console.log(a); // 99
console.log(b); // 2


배열 분해 할당을 통해 두 변수의 값을 쉽게 변경 교환할 수 있다.


let a = 10;
let b = 20;
[a, b] = [b, a];
console.log(a); // 20
console.log(b); // 10


배열 분해 할당에서 배열 속성의 순서를 쉼표를 입력해 설정할 수 있다.

const arr = [10, 20, 30];
const [a, , c] = arr;
console.log(a); // 10
console.log(b); // 30


추출한 속성 개수만큼을 제외한 나머지로 새로운 배열을 생성할 수 있다.


const arr = [10, 20, 30];
const [first, ...rest1] = arr;
console.log(rest1); // [20, 30]
const [a, b, c, ...rest2] = arr;
console.log(rest2); // []

위와 같이 배열 분해 할당 시 마지막에 ... 을 통해 변수에 나머지 모든 속성값으로 새로운 배열을 만든다.

또한 나머지가 없으면 빈 배열을 생성한다.



#2 Object destructuring assignment(객체 분해 할당)


객체의 여러 속성값을 변수로 분해하여 쉽게 할당할 수 있는 문법이다.


const obj = { gender: 'Male', name: 'Peter' };
const { gender, name } = obj;
console.log(gender); // male
console.log(name); // peter

객체는 속성명을 그대로 가져와 할당하기 때문에 순서는 중요하지 않다.


따라서 배열 분해 할당에서는 변수명을 변경할 수 있었지만 객체 분해 할당에서는 그대로 사용해야 한다.


const obj = { gender: 'Male', name: 'Peter' };
const { gender, name } = obj; // 1
const { name, gender } = obj; // 2
const { a, b } = obj; // 3

1, 2의 결과는 동일하다. 3은 존재하지 않은 속성명을 사용하여 undefined가 할당 된다.


새로운 변수명에 할당도 가능하다.


const obj = { gender: 'Male', name: 'peter' };
const { gender: sex, name } = obj;
console.log(sex); // 'male'
console.log(gender) // 참조 에러

obj의 속성 gendersex 변수에 할당된다. gender라는 변수는 생기지 않고 gender의 값이 sex 변수에 할당된다.


배열 분해 할당처럼 객체 분해 할당에서도 기본값을 정의 할 수 있다.


const obj = { gender: undefined, name: null, age: 21 };
const { gender = 'none', name = 'none', age = 0 } = obj;
console.log(gender); // none 1
console.log(name); // null 2
console.log(age) // 21

1의 genderundefined이기 때문에 defaultnone을 할당한다.

2의 namenull이기 때문에 null 값을 할당한다. 명심하자.


변수명 사용자 정의와 default(기본값) 설정을 동시에 해보자.


const obj = { gender: undefined, name: 'Peter'};
const { gender: sex = 'none', name} = obj;
console.log(sex); // 'none'


또한 기본값으로 함수의 return 값을 넣을 수 있다.

function getDefaultText() {
console.log('getDefaultText');
return 'none';
}
const obj1 = { gender: undefined, name: 'Peter'};
const { gender = getDefaultText(), name} = obj1;
// 'getDefaultText' 출력

const obj2 = { address: 'Seoul', empNo: 00001 };
const { address = getDefaultText(), empNo} = obj2;
// 'getDefaultText' 출력 되지않음

당연한 결과지만 default 값을 지정할 경우에만 함수가 호출된다.


객체 분해 할당 시 할당되지 않은 값들로 하나의 별도의 개체를 만들 수 있다.

const obj = {gender: 'Male', name: 'Peter', age: 24};
const { name, ...rest } = obj;
console.log(rest); // { gender: 'male', age: 24 }

배열 분해 할당과 비슷한 방식이다.


for of 문 사용 시 객체 분해 할당을 활용해 보자


const userList = [
{ gender: 'Male', name: 'Peter', age: 24},
{ gender: 'Female', name: 'Jane', age: 24},
]
for(const {gender, name} of userList) {
console.log(name + " : " + gender);
}



객체 속 객체도 분해 가능하다.

const obj = { name: 'Peter', friend: { name: 'Jane'} };
const {
name,
friend: { name: friendName },
} = obj;
console.log(name); // 'peter'
console.log(friendName); // 'Jane'
console.log(friend); // 참조 에러

객체 분해 할당을 통해 friendName이라는 변수만 생성된다.


분해 할당을 통해 변수 단위만 정의할 수 있는 것은 아니다. 패턴 단위로 분해 할당이 가능하다.


const [{ prop: a } = { prop: 111 }] = []; // 1
console.log(a); // 111
const [{ prop: b } = { prop: 111 }] = [{}]; // 2
console.log(b); // undefined

1은 배열에서 첫 번째 원소가 { prop: a }이며, 기본값을 { prop: 111 }로 정의했다. 객체 분해할 [ ]의 첫 번째 원소가 없으므로 기본값이 할당된다.

2에서는 객체 분해할 [{ }]의 첫 번째 원소가 {}이며 prop 속성명은 존재하지 않으므로 bundefined가 할당된다.


객체 분해 할당에서 계산된 속성명을 활용할 수 있다.


const idx = 1;
const { [`key${idx}`]: valOfIdx } = { key1: 111 };
console.log(valOfIdx); // 111

객체 분해 할당에서 계산된 속성명을 사용할 때에는 반드시 별칭을 입력해야 한다.


별칭은 단순히 변수명만 입력할 수 있는 것은 아니다.


const obj = {};
const arr = [];
({ a: obj.prop, b: arr[0]} = { a: 111, b: true });
console.log(obj); // { prop:111 }
console.log(arr); // [ true ]

이처럼 객체의 속성명 또는 배열의 첫 번째 원소에 값을 할당할 수 있다.

블로그 이미지

steadily

,

ES6+ 향상된 Object, Array (1)


1. 객체와 배열의 생성과 수정을 간편하게.


#1 Shorthand property names (단축 속성명)


Literal Code를 간편하게 작성


const name = 'peter';
const obj = {
gender: 'male',
name, // 이미 정의 된 변수면 변수명 입력 {name: 'peter'}
getName() { return this.name},
// 함수형은 function 키워드 생략 { getName : function(){....}}
}

비교를 통해 어떻게 개선 되었는지 알아보자.


// 기존 함수 정의
function createUser(name, gender) {
return { name: name, gender: gender};
}
// 개선된 방법
function createUser(name, gender) {
return { name, gender};
}

이렇게 단축 속성명을 사용하면 코드 작성도 간편하고 가독성도 좋아진다.


아래와 같이 디버깅 시 많이 개선된 점을 확인할 수 있다.


const name = 'peter';
const gender = 'male';
// 기존방식
console.log('name =' + name, ', gender =' + gender);
// ES6+
console.log({ name, gender }); // { name: 'peter', gender: 'male' }



#2 Computed property names (계산된 속성명)


더 편리하게 동적으로 속성명을 결정할 수 있게 되었다.

계산된 속성명을 사용한 코드와 그렇지 않은 코드를 비교해보자.


function createObj1( key, val ) {
const obj = {};
obj[key] = val;
return obj;
}
function createObj2( key, val ) {
return { [key]: val };
}

계산된 속성명을 사용하면 createObj2 함수처럼 간결하게 작성할 수 있다.


이 기능을 컴포넌트의 상탯값을 변경할 때 사용하면 유용하다.


class CntComponent extends React.Component {
state = {
cnt1: 0,
cnt2: 0,
cnt3: 0,
};
// ...
onClick = idx => {
const key = `cnt${idx}`;
const val = this.state[key];
this.setState({ [key]: val + 1 }); // 1
}
}

1번에서 복잡해질수 있는 코드를 계산된 속성명을 사용하여 간결하게 작성하였다.




블로그 이미지

steadily

,

ES6+ 변수를 정의하는 새로운 방법 ( const, let )

새로운 방법이 나온 이유는 기존 방법으로는 해결되지 않는 문제가 있었기 떄문이다.

ES6 이전 var가 가진 문제들을 먼저 알아보자.


#1 var가 가진 문제 (함수 스코프)


스코프(scope) = 변수가 사용될 수 있는 영역

스코프는 변수가 정의된 위치에 의해 결정 된다.

var로 정의한 변수는 함수 스코프이기 때문에 아래와 같이 함수를 벗어난 영역에서 사용하면 에러가 발생한다.



function example() {
    var i = 1;
}
console.log(i); // 참조 에러


var 변수를 함수가 아닌 프로그램의 가장 바깥에 정의하면 전역 변수가 되는데, 이는 프로그램 전체를 감싸는 하나의 함수가 있다고 생각하면 이해가 쉽다.

하나 특이한 점은 함수 안에서 var 키워드를 사용하지 않고 변수에 값을 할당하면 그 변수는 전역 변수가 된다.



function example1() {
    i = 1;
}
function example2() {
    console.log(i);
}
example1();
example2(); // 1이 출력 됨.


이런 상황에서 명시적 에러가 발생하도록 하려면 파일 상단에 'use strict'를 선언하면 된다.

var는 함수 스코프이기 때문에 for 반복문에서 정의된 변수가 반복문이 끝난 이후에도 계속 남는 문제점이 있다.



for ( var i = 0; i < 10; i++ ) {
    console.log(i);
}
console.log(i); // 10


for 문뿐만 아니라 while 문, switch 문, if 문 등 함수 내부에서 작성되는 모든 코드는 같은 문재를 안고 있다.

var 변수의 스코프를 제한하기 위해 즉시 실행 함수를 사용하기도 한다. 즉시 실행 함수는 함수를 정의하는 시점애 바로 실행되고 사라진다.

var 변수는 함수 스코프이므로 즉시 실행 함수로 묶으면 변수의 스코프를 제한할 수 있다. 그러나  즉시 실행 함수는 작성하기 번거롭고 가독성도 떨어진다.



(function () {
    // statements
})()

var 변수의 스코프 문제를 해결하려면 이렇게 상당한 노력이 필요하다.


#2 var의 두번째 문제: 호이스팅

var로 정의된 변수는 그 변수가 속한 스코프의 최상단으로 끌어올려진다. 이를 호이스팅(hoisting)이라고 부른다.

끌어올려진다는 말의 의미가 무엇인지 지금부터 살펴보자.


다음 코드에서는 정의되지 않은 변수를 사용해서 에러가 발생한다.


console.log(myVar); // 참조 에러



이제 console.log 밑에 변수를 정의해 보자.


console.log(myVar); // undefined
var myVar = 1;


변수를 정의하기 전에 사용했음에도 위 코드를 실행하면 에러가 발생하지 않는다.

특이한 점은 1일 아니라 undefined가 출력된다는 점이다.

이것은 해당 변수의 정의가 위쪽으로 끌어올려졌기 때문이다.


다음처럼 변경됐다고 생각하면 이해하기 쉽다.



var myVar = undefined;
console.log(myVar); // undefined
myVar = 1;


변수의 정의만 끌어올려지고 값은 원래 정의했던 위치에서 할당된다.

특이하게도 다음처럼 변수가 정의된 곳 위에서 값을 할당할 수도 있다.


console.log(myVar); // undefined
myVar = 2;
console.log(myVar); // 2
var myVar = 1;


버그처럼 보이는 위 코드는 에러 없이 사용될 수 있는 것은 단점이라고 할 수 있다.

호이스팅은 직관적이지 않으며, 보통의 프로그래밍 언어에서는 찾아보기 힘든 성질이다.


#3 var의 기타 문제들

var의 또 다른 문제를 살펴보자. var를 이용하면 한 번 정의된 변수를 재정의할 수 있다.



var myVar = 1;
var myVar = 2;


변수를 정의한다는 것은 이전에 없던 변수를 생성한다는 의미로 통용된다. 

따라서 앞의 코드가 에러 없이 사용될 수 있다는 것은 직관적이지 않으며 버그로 이어질 수 있다.


또 다른 문제는 var가 재할당 가능한 변수로밖에 만들 수 없다는 점이다. 

상수처럼 쓸 값도 무조건 재할당 가능한 변수로 만들어야 한다. 

이런 상황에서 재할당 불가능 한 벼수를 사용한다면 코드의 복잡도가 낮아지고 가독성은 높아진다.




var의 문제를 해결하는 const, let


#1 const, let은 블록 스코프다.

var = 함수 스코프

const, let = 블록(block) 스코프

함수 스코프의 단점 대부분이 블록 스코프에는 없다.

블록 스코프는 대부분의 언어에서 사용하므로 개발자에게 익숙한 개념이다.


블록 스코프에서는 블록을 벗어나면 변수를 사용할 수 없다.


if (true) {
    const i = 0;
}
console.log(i); // 참조 에러


if 문의 블록 안에서 정의된 변수는 if 문을 벗어나면 참조할 수 없다.

따라서 if 문에서 생성된 변수를 블록 바깥에서 사용하려고 하면 에러가 발생한다.

이러한 상황에서 에러가 발생하는 것이 직관적이며 이해하기도 쉽다.

var를 사용하는 경우에는 if 문 안에서 생성된 변수가 if 문을 벗어나도 계속 살아 있기 때문에,

함수 스코프를 벗어나기 전까지는 계속해서 신경 써서 관리해야 한다.


이번에는 블록 스코프에서 같은 이름의 변수를 정의하는 경우를 살펴보자.


let foo = 'bar1';
console.log(foo); // bar1
if (true) {
    let foo = 'bar2';
console.log(foo); // bar2
}
console.log(foo); // bar1

마지막 줄의 foo 변수는 같은 블록에서 정의된 첫 줄의 변수를 참조하므로 'bar1'을 출력한다.


#2 const, let에서의 호이스팅

const 또는 let으로 정의된 변수도 호이스팅이된다.

하지만, const 또는 let으로 변수를 정의하기 전에 그 변수를 사용하려고 하면 참조 에러가 발생한다.



console.log(foo); // 참조 에러
const foo = 1;


따라서 const 또는 let으로 정의한 변수는 호이스팅이 되지 않는다고 생각하기 쉽다.

하지만 const 또는 let으로 정의된 변수도 호이스팅이된다.

다만 변수가 정의된 위치와 호이스팅된 위치 사이에서 변수를 사용하려고 하면 에러가 발생한다. 

이 구간을 임시적 사각지대(temporal dead zone)라고 한다.


임시적 사각지대에서 변수를 사용하지 못한다면 호이스팅의 역할은 무엇인지 생각해 보자.

다음 코드에서는 같은 이름의 변수가 서로 다른 스코프에 정의되어 있다.



const foo = 1; // 2
{
   console.log(foo); // 3 - 참조 에러
const foo = 2; // 1
}

만약 

1번 변수가 호이스팅되지 않았다면 => 3번 변수의 참조 에러는 발생하지 않고 2번 변수의 값이 출력될 것이다.

이것으로 호이스팅의 역할을 짐작할 수 있다.

1번 변수의 호이스팅 때문에 3번 변수는 1번 변수를 참조하게 된다.

그리고 1번 변수를 참조했지만 임시적 사각지대여서 에러가 발생한다.


이를 제대로 이해하기 위해서는 브라우저가 자바스크립트의 실행 환경을 어떻게 구축하는지 알아야 하는데

이번 주제의 범위를 벗어나므로 자세한 설명은 생략한다.


var로 정의된 변수에는 임시적 사각지대가 없기 때문에 다음 코드에서는 참조 에러가 발생하지 않는다.


var foo = 1;
(function() {
    console.log(foo); // undefined
var foo = 2;
})();



#3 const는 변수를 재할당 불가능하게 만든다.

const로 정의된 변수는 재할당이 불가능하다.

반대로 let, var로 정의된 변수는 재할당할 수 있다.

재할당 불가능한 변수는 프로그램의 복잡도를 상당히 낮춰주기 때문에 되도록이면 재할당 불가능한 변수를 사용하는게 좋다.


const bar = 'a';
bar = 'b'; // 에러 발생 1
var foo = 'a';
foo = 'b'; // 에러 없음
let value = 'a';
value = 'b'; // 에러 없음

1번 처럼 const로 정의된 변수에 값을 재할당하면 에러가 발생한다.


다만 const로 정의된 객체의 내부 속성값은 수정 가능하다는 점을 주의해야 한다.


const bar = { prop1: 'a'};
bar.prop1 = 'b';
bar.prop2 = 123;
console.log(bar); // { prop1: 'b', prop2: 123 }
const arr = [10, 20];
arr[0] = 100;
arr.push(300);
console.log(arr); // [100, 20, 300]

이미 존재하는 속성값을 수정하거나 새로운 속성값을 추가하는 것 모두 가능하다.

객체의 내부 속성값도 수정 불가능하게 만들고 싶다면 immer, immutable.js등의 외부 패키지를 활용하는 게 좋다.

이러한 외부 패키지는 객체를 수정하려고 할 때 기존 객체는 변경하지 않고 새로운 객체를 생성한다.


새로운 객체를 생성하는 기능은 필요 없고 단지 수정만 할 수 없도록 차단하고 싶다면, 다음과 같은 javascript 내장 함수를 이용하면 된다.


Object.preventExtensions

Object.seal

Object.freeze


당연한 이야기지만 const로 정의했다면 객체를 참조하는 변수 자체를 변경하는 것은 불가능하다.


const bar = { prop1: 'a'};
bar = { prop2: 123}; // 에러 발생


블로그 이미지

steadily

,