이전 글
객체 자체를 먼저 다뤘어야 했는데, 어쩌다보니 순서가 좀 뒤바뀌게 되었다.
이번 포스팅에서는 자바스크립트에서의 일반 객체(순수 객체)에 대해 정리한다.
객체
자바스크립트에서 객체는 중괄호 {} 를 이용해 만들 수 있다.
각 원소들은 각각 key:value 쌍으로 구성된다.
key는 문자형이어야만 하고 value로는 모든 자료형이 허용된다.
객체의 원소는 프로퍼티(property)라고 하며,
key는 이름으로 value를 찾기 위한 라벨이라고 볼 수 있다.
객체를 생성하는 대표적인 두가지 방법을 소개하면,
const obj = new Object(); // 객체 생성자
const obj = {}; // 객체 리터럴
이 두가지가 있다.
new 키워드를 이용하는 생성자 방법
과 중괄호를 이용해 선언하는 리터럴 방법
이다.
객체 프로퍼티의 생성, 불러오기, 추가, 삭제
생성하기
const player = {
name : 'Tom',
age : 12,
level : 7,
'soul food' : 'banana',
'created_at' : '20220111',
};
player 객체가 존재한다고 할 때, 아래와 같이 해석한다.
객체 player는 객체 리터럴로 선언되었다.
5개의 프로퍼티가 있고, 키 값으로 name, age, level, soul food, created_at이 있으며 그에 해당하는 값들이 존재한다.
불러오기
객체를 불러오는 다양한 방법이 있다.
console.log(player.name); // O
console.log(player['name']); // O
console.log(player.created_at); // O
console.log(player['created_at']); // O
console.log(player['soul food']); // O
console.log(player[name]); // X
console.log(player.soul food) // X
가능한 방법과 가능하지 않은 방법을 표시해뒀다.
점 표기법(dot notation)을 이용하거나, 대괄호 표기법 []을 이용해 값을 조회할 수 있다.
점 표기법을 이용할 때는 key가 유효한 변수 식별자여야 한다.
※ 유효한 변수 식별자란?
공백이 없어야 하며, 숫자로 시작하지 않아야 하고 $ _ 를 제외한 특수 문자를 포함할 수 없다.
그래서 soul food와 같이 여러 단어가 조합된 키는 대괄호 표기법 []을 이용해야 한다.
대괄호 표기법 [] 안에는 점 표기법과 동일하게 키 이름을 적어주는데, 반드시 문자열로 적어줘야 한다.
문자열이 아닌 변수를 넣어주면, 런타임 시에 변수가 평가되어 변수에 할당된 값을 키 이름으로 취급하여 조회하게 된다.
이건 코드를 통하면 쉽게 이해가 될 듯 하다.
const obj = {
name : 'Tom',
Tom : 'its not name',
};
const name = 'Tom';
console.log(obj.name) // 'Tom'
console.log(obj['name']) // 'Tom'
console.log(obj[name]) // 'its not name' (런타임 시 name 변수값을 이용해서 obj['Tom'] 으로 평가된다.)
추가하기
const obj = {
name : 'Tom'
};
obj.age = 17;
obj['food'] = 'banana';
obj[hobby] = 'tennis'; // X
객체에 프로퍼티를 추가하는 것도 마찬가지다.
엄밀히 말하면, 기존에 해당 키 이름이 존재하면 값의 변경이 되며, 키 이름이 존재하지 않으면 생성(추가)가 된다.
마지막에 적힌 obj[hobby]는 오류가 발생하는데, hobby 변수가 존재하지 않기 때문이다.
키 이름이 hobby인 프로퍼티를 생성하고 싶다면, obj['hobby'] 로 작성해야 한다.
삭제하기
const obj = {
name : 'Tom',
age : 17
};
delete obj.name;
console.log(obj); // {age : 17}
delete 키워드를 이용하면, 객체에서 프로퍼티를 삭제할 수 있다.
대괄호 표기법 응용하기
대괄호 표기법 []은 생각보다 더 강력하다.
런타임 시 사용자로부터 입력받은 값 등을 변수를 이용하면 변수의 값에 해당하는 키 이름을 설정할 수 있다.
const name = 'Tom';
const userObj = {};
userObj[name] = 1;
console.log(userObj) // {Tom : 1}
변수 name 값이 런타임 시 평가되어 userObj[name]이 userObj['Tom'] 으로 평가되어 key:value로 'Tom' : 1 을 생성한다.
이렇게 변수가 평가된 값을 이용한 프로퍼티 생성이 이루어짐을 응용하면,
const nameList = ['Tom','Juliet','Jack','Whale'];
const nameObj = {};
nameList.forEach(name => nameObj[name] = 1);
console.log(nameObj); // {Tom: 1, Juliet: 1, Jack: 1, Whale: 1}
다음과 같이 각 리스트의 원소들을 key 이름으로 가지도록 할 수도 있다.
점 표기법을 이용해서 이 예시와 같은 객체를 만드는 작업은 아래와 같은데
const nameObj = {};
name.Tom = 1;
name.Juliet = 1;
name.Jack = 1;
name.Whale = 1;
console.log(nameObj); // {Tom: 1, Juliet: 1, Jack: 1, Whale: 1}
다행히 원소가 4개라서 망정이지, 10개만 넘어가도 답이 없음은 분명하다.
계산된 프로퍼티
객체 리터럴 {} 로 객체를 생성할 때, 프로퍼티의 키 이름을 대괄호로 둘러 쌀 수 있다.
이를 계산된 프로퍼티(Computed Property) 라고 한다.
const hobby = prompt('좋아하는 운동은?');
const favorite = {
[hobby] : true
};
console.log(favorite); // { tennis : true }
대충 사용자가 브라우저에서 prompt에 좋아하는 운동으로 tennis을 입력했다고 하면, 결과는 위와 같다.
물론, 위 코드는 아래 코드와 동일하게 동작한다.
const hobby = prompt('좋아하는 운동은?');
const favorite = {};
favorite[hobby] = true;
console.log(favorite); // { tennis : true }
추가로 변수 값을 이용해서 프로퍼티를 생성할 때,
기존 변수값에 추가하고 싶은 문자열 등을 추가해서 키 이름으로 생성할 수도 있다.
const name = 'Tom';
const comic = {
[name + 'AndJerry'] : 'NO!',
};
console.log(comic); // { TomAndJerry : 'No!' }
// 혹은 이렇게도 가능
const nameList = ['Tom', 'Jane', 'Kane'];
const team = {};
nameList.forEach(name => {
team[name + 'hobby'] = 'soccer';
team[name + 'year'] = 22;
});
console.log(team); // {Tomhobby: 'soccer', Tomyear: 22, Janehobby: 'soccer', Janeyear: 22, Kanehobby: 'soccer', …}
이와 같이 [변수 + '추가하고자 하는 문자열'] 의 조합이 가능하다.
런타임 시 변수가 문자열로 평가되므로, ['문자열' + '문자열'] = ['이어 붙인 문자열'] 이 되는 원리이다.
키, 값이 중복될 시 단축이 가능
기존 변수 값들을 프로퍼티로 삼아 객체를 새로 생성해야 하는 상황이 있다.
const name = 'Tom';
const age = 17;
const user = {
name:name,
age:age
};
// 이와 같이 key:value의 값이 겹친다면 단축이 가능하다.
const shortUser = {
name,
age
};
name:name 과 단축된 name은 같은 역할을 한다.
name:name 에서 콜론(:)의 왼쪽 name은 키 이름에 해당하며 우측 name은 변수값에 해당한다.
즉 현재 변수 name 값이 'Tom' 이므로 name:'Tom' 이 생성되며,
단축된 name으로 프로퍼티를 선언해도 위와 동일하게 생각하면 된다.
이렇게 단축 프로퍼티를 사용하면?
const obj = {
name:name,
age:age,
hobby:hobby
};
// 위의 코드를
const obj = {name, age, hobby};
// 이렇게 획기적으로 줄일 수 있다. 👍
키 이름으로 숫자 사용하기
키 이름으로 숫자를 사용할 수도 있다.
여기서는 위에서 설명했던 방법과 조금 다를 수 있겠다.
const obj = {
0: 'Tom',
1: 'Jane',
2: 'Kane',
};
console.log(obj['0']); // O
console.log(obj[0]); // O
console.log(obj.0); // X
obj['0'] 의 경우, '0'의 키 이름을 조회하므로 정상 동작한다.
obj[0]의 경우, 숫자 0은 변수가 될 수 없으므로 문자열로 자동 형변환이 된다.
그러므로 obj[0] = obj['0'] 취급되어 위와 동일하게 정상 동작한다.
obj.0의 경우, 앞서 언급한 유효한 변수 식별자에 어긋난다.
dot notation(.) 바로 뒤에 숫자로 시작할 수 없다.
객체에 특정 프로퍼티가 존재하는지 확인하기
객체에 특정한 프로퍼티 키 이름이 존재하는지 확인해야 하는 상황이 종종 있다.
필자는 그럴 때 마다 프로퍼티가 존재하지 않는 것을 검출하기 위해서if(!obj.name) blahblah
와 같은 방식을 사용했으나
in 연산자로 존재 여부 확인이 가능하다.
const obj = {
name : 'Tom',
age : 17
};
console.log(!obj.name) // false;
console.log(!obj.hobby) // true;
console.log('name' in obj); // true
console.log('hobby' in obj); // false
사실 !obj.hobby === !undefined === true
이기 때문에, 현재는 기능적으로 문제는 없는 것 처럼 보이지만
const obj = {
name : 'Tom',
age : 17,
hobby : undefined
};
console.log(!obj.hobby); // true
console.log('hobby' in obj) // true
이렇게 hobby 키를 가진 프로퍼티가 존재할 수도 있기 때문에, 완벽한 검증이 아닐 수 있다.
또한 여기서도 문자열 in 객체
형식으로 'hobby' in obj
형태를 이용해야 한다.hobby in obj
를 타이핑해서 엉뚱한 hobby 변수 값을 조사해서 에러를 일으키지 말자.
객체 순회하기
객체도 배열처럼 순회할 수 있다.
for in 문법을 이용하면 된다.
const obj = {
name : 'Tom',
age : 17,
hobby : 'tennis'
};
for(let key in obj){
console.log(key, obj[key]);
};
// name Tom
// age 17
// hobby tennis
다음과 같이 in 키워드로 키를 조회하며, 객체[키]로 값을 함께 조회할 수 있다.
필자는 Object의 메서드를 이용한 조회를 더 많이 사용한다.
Object.keys - 모든 키를 포함한 1차원 배열 반환
Objecy.values - 모든 값을 포함한 1차원 배열 반환
Object.entries - [키, 값] 쌍의 2차원 배열 반환
const obj = {
name : 'Tom',
age : 17,
hobby : 'tennis'
};
console.log(Object.keys(obj)); // ['name', 'age', 'hobby']
console.log(Object.values(obj)); // ['Tom', 17, 'tennis']
console.log(Object.entries(obj));
/*
[
['name', 'Tom'],
['age', 17],
['hobby', 'tennis']
]
*/
객체 사용시 유의점
객체와 같은 참조값을 사용할 시 유의할 점이 있다.
const name = 'Tom';
const obj = {
name:'Tom'
};
name = 'Jane' // X
obj = {
name : 'Jane
}; // X
obj.name = 'Jane' // O
const 키워드로 선언한 변수는 재할당이 불가능하다.
즉 새로운 값으로 바꿀 수 없다.
위와 같이 name, obj 변수에 재할당이 불가능하지만, obj의 프로퍼티에 대한 변경은 가능하다.
obj 변수에는 '객체 자체'가 할당된 것이 아니라, '객체의 주소값' 이 할당되어 있어서
const 키워드는 '주소값'을 기억하고 있기 때문에 '주소값'을 변하게 하는 재할당이 불가능하지만, 객체의 프로퍼티를 변경이 될 수 있다.
즉, obj 객체의 껍질 자체는 불변이지만 obj 객체의 내부 프로퍼티들은 불변이 아니라고 이해하면 되겠다.
또한 이와 같이 obj 변수가 '주소값' 임을 이해하게 되면, 한가지 의문점을 더 해결할 수 있는데
const user = {
name:'Tom',
phone:01000000000
};
function takeANumber(customer){
customer.number = 0;
console.log(customer);
}
takeANumber(user); // {name: 'Tom', phone: 134217728, number: 0}
console.log(user); // {name: 'Tom', phone: 134217728, number: 0}
위와 같이 함수 매개변수로,
user를 넣어줬을 때 '복사' 되었다고 착각할 수 있는데
매개변수로 넣어준 user 객체에 name 프로퍼티가 생성되는 상황이 왜 발생하는지 이해하게 된다.
takeANumber에서 customer 매개변수가 user의 주소 값을 가지고 있다.
그래서 주소값에 있는 객체에 number:0의 프로퍼티를 생성한 것일 뿐이다.
만약 기존 객체를 변경하지 않으면서 takeANumber 함수를 이용하고 싶다면,
다양한 방법이 있겠지만 가장 간단한 방법으로는 spread operator를 사용하길 권장한다.
const user = {
name:'Tom',
phone:01000000000
};
function takeANumber(customer){
customer.number = 0;
console.log(customer);
}
takeANumber({...user}); // {name: 'Tom', phone: 134217728, number: 0}
console.log(user); // {name: 'Tom', phone: 134217728}
spread operator는 '전개구문' 이라 부르며,
이름 그대로 해당 객체 / 배열 등을 풀어준다고 생각하면 된다.
물론 이 방법이 완전한 것은 아니다.
'얕은 복사'를 수행하기 때문에, 객체 혹은 배열 내에 값으로 또 다른 배열이나 객체를 포함한다면 다른 방법을 사용해야 한다.
출처
'JavaScript > theory' 카테고리의 다른 글
래퍼 객체(Wrapper Object)란? (0) | 2022.01.14 |
---|---|
[자바스크립트] V8 엔진의 메모리 관리 이해하기 (0) | 2022.01.12 |
[자바스크립트] 고차함수와 배열 내장 메서드 (0) | 2022.01.06 |
[함수형] 클로저 Closure에 대해 알아보자. (0) | 2022.01.06 |
[객체] 자바스크립트에서 객체를 생성하는 다양한 방법 (0) | 2022.01.04 |