일시: 2022.10.9
참석자: 윤수지, 김현수, 송여진, 유승연, 유효주, 조민재, 신유정
GitHub - GDSC-Ewha-4th/Study-Javascript: Javascript Deep Dive Study | GDSC Ewha 4th
Javascript Deep Dive Study | GDSC Ewha 4th. Contribute to GDSC-Ewha-4th/Study-Javascript development by creating an account on GitHub.
github.com
금주 스터디는 송여진님께서 정리하신 내용을 공유해주셨습니다.
1. 데이터 타입
1.1. 데이터 타입의 종류
자바스크립트의 데이터 타입에는 두 가지가 있다. 기본형 primitive type
과 참조형 reference type
이다.
기본형
- 숫자
number
, 문자열string
, 불리언boolean
, null, undefined 등
참조형
- 객체
object
- 배열
Array
- 함수
Function
- 날짜
Date
- 정규표현식
RegExp
- 배열
기본형은 값이 담긴 주솟값을 바로 복제하는 반면, 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다.
또한 기본형은 불변성 immutability
을 띈다.
1.2. 데이터 타입에 관한 배경지식
0 또는 1만 표현할 수 있는 하나의 메모리 조각을 비트 bit
라고 한다. 메모리는 매우 많은 비트로 구성되어 있는데, 각 비트는 고유한 식별자를 통해 위치를 확인할 수 있다. 하지만 비트 단위로 위치를 확인하는 것은 매우 비효율적이다. 따라서 많은 비트를 한 단위로 묶은 것이 바이트 byte
이다.
모든 데이터는 바이트 단위의 식별자, 메모리 주솟값 memory address
을 통해 서로를 구분할 수 있다.
1.3. 변수 선언과 데이터 할당
기본적인 변수 선언식은 다음과 같다.
var a;
변수는 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇이라고 말할 수 있다.
변수를 선언하면 컴퓨터는 메모리에서 빈 공간 하나를 확보한다. 그리고 그 공간의 이름(식별자)를 a
라고 정한다.
이후의 사용자가 a에 접근하고자 하면, 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해서 해당 공간에 담긴 데이터를 반환할 것이다.
var a;
a = 'abc';
var a = 'abc'; // 변수 선언과 할당을 한 문장으로 표현
데이터 할당의 전체 흐름은 다음과 같다.
- 변수 영역에서 빈 공간(@1003)을 확보한다.
- 확보한 공간의 식별자를 a로 지정한다.
- 데이터 영역의 빈 공간(@5004)에 문자열 'abc'를 저장한다.
- 변수 영역에서 a라는 식별자를 검색한다.(@1003)
- 앞서 저장한 문자열의 주소(@5004)를 @1003에 대입한다.
😒 왜 굳이 번거롭게 변수 영역에 바로 값을 대입하지 않고 한 단계를 더 거치는 이유는 무엇일까?
만약 해당 공간 내에서만 데이터 변환을 할 수 있다면 공간을 데이터 크기에 맞게 늘리는 작업이 진행되어야 하고, 매우 비효율적이다.
1.4. 기본형 데이터와 참조형 데이터
불변값
변수 variable
과 상수 constant
를 구분하는 성질은 '변경 가능성'이다.
var a = 'abc';
a = a + 'def';
var b = 5;
var c = 5;
b = 7;
예시를 보면 맨 처음 변수 a에 'abc'를 할당했다가 뒤에 'def'를 추가한다. 이렇게 하면 기존의 'abc'가 'abcdef' 로 바뀌는 것이 아니라 새로운 문자열 'abcdef'를 만들어 그 주소를 a에 저장한다.
변수 b에 5를 할당하고 다시 변수 c에 5를 할당할 때에는 기존의 값이 있으니 이를 재활용한다.
b의 값을 7로 바꿀 때에는 기존에 저장했던 7을 찾아서 있으면 재활용하고, 없으면 새로 만들어 b에 저장한다.
가변값
참조형 데이터의 기본적인 성질은 가변값인 경우가 많지만, 설정에 따라 변경 불가능한 경우도 있다.
var obj1 = {
a: 1,
b: 'bbb'
};
참조형 데이터의 할당 과정은 다음과 같다.
- 컴퓨터는 우선 변수 영역의 빈 공간(@1002)을 확보하고, 그 주소의 이름을 obj1로 지정한다.
- 임의의 데이터 저장 공간(@5001)에 데이터를 저장하려고 하니 프로퍼티로 이뤄진 데이터 그룹이다. 따라서 이 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고 그 영역의 주소(@7103 ~ ?)을 @5001에 저장한다.
- @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정한다.
- 데이터 영역에서 숫자 1을 검색한다. 검색 결과가 없으므로 임의로 @5003에 저장하고 이 주소를 @7103에 저장한다. 문자열 'bbb'역시 임의로 @5004에 저장하고 이 주소를 @7104에 저장한다.
기본형 데이터와 다른 점은 '객체의 변수(프로퍼티) 영역' 이 별도로 존재한다는 것이다.
참조형 데이터의 프로퍼티 재할당에 대한 예시를 살펴보자.
var obj1 = {
a: 1,
b: 'bbb'
};
obj1.a = 2;
obj1의 a 프로퍼티에 숫자 2를 할당하려고 한다. 데이터 영역에서 숫자 2를 찾고 없으므로 빈 공간인 @5005에 저장하고, 이 주소를 @7103에 저장한다.
명령 전후에 변수 obj1이 바라보고 있는 주소는 @5001로 변하지 않았다. 기존의 객체 내부의 값만이 바뀐 것이다.
이번에는 참조형 데이터의 프로퍼티에 다시 참조형 데이터를 할당하는 경우를 살펴보자.
var obj = {
x:3,
arr: [3, 4, 5]
};
자세한 할당 과정은 책의 12~13p를 참고하면 된다.
변수 복사 비교
var a =10;
var b = a;
var obj1 = { c: 10, d: 'ddd' }
var obj2 = obj1;
변수를 복사하는 과정은 동일하지만, 데이터 할당 과정에서 방법의 차이가 발생하기 때문에 변수 복사 이후의 동작에도 큰 차이가 발생한다.
따라서 a !== b
이고, obj1 == obj2
라고 할 수 있다.
기본형과 참조형 데이터의 가장 큰 차이는 기본형은 주솟값을 복사하는 과정이 한 번만 이뤄지고, 참조형은 한 단계를 더 거치게 된다는 차이가 있는 것이다.
1.5. 불변 객체(immutable object)
불변 객체는 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우에 필요하다.
예시 코드를 보자.
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function (user, name) {
var newUser = user;
newUser.name = newName;
return newUser;
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name);
console.log(user == user2);
이 에시는 객체의 가변성으로 인한 문제점을 보여주는 예시이다. 정보 변경 전과 후에 같은 객체를 사용하므로 코드가 제대로 동작하지 않는다.
문제점을 해결한 코드는 다음과 같다.
var user = {
name: 'Jaenam',
gender: 'male'
};
var changeName = function (user, name) {
return {
name: newName,
gender: user.gender
};
};
var user2 = changeName(user, 'Jung');
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name);
console.log(user == user2);
changeName 함수가 새로운 객체를 반환하도록 수정하였다. 이제 user와 user2는 서로 다른 객체이므로 변경 전후를 안전하게 비교할 수 있다.
다음은 기존 정보를 복사해서 새로운 객체를 반환하는 함수이다.
var copyObject = function (target) {
var result = {};
for ( var prop in target ) {
result[prop] = target[prop];
}
return result;
};
copyObject
는 for in 문법을 이용해 result 객체에 target 객체의 프로퍼티들을 복사하는 함수이다.
var user = {
name: 'Jaenam',
gender: 'male'
};
var user2 = copyObject(user);
user2.name = 'Jung';
if (user !== user2) {
console.log('유저 정보가 변경되었습니다.');
}
console.log(user.name, user2.name); // Jaenam Jung
console.log(user == user2); // false
copyObject
함수를 통해 간단하게 객체를 복사하고 내용을 수정하는 데 성공하였다.
이제부터 user 객체 내부의 변경이 필요할 때에는 무조건 copyObject
를 사용하기로 합의하였다.
얕은 복사와 깊은 복사
얕은 복사 shallow copy
는 바로 아래 단계의 값만 복사하는 방법이고, 깊은 복사 deep copy
는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다. copyObject
함수는 얕은 복사만 수행하였다.
이 말은 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사한다는 의미이다. 얕은 복사로 코드를 복사하면 한 단계 아래의 내부 프로퍼티들은 복사되지 않는다.
깊은 복사에 대한 예시 코드는 다음과 같다.
var user2 = copyObject(user);
user2.urls = copyObject(user.urls);
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio == user2.urls.portfolio); // false
user2.urls.blog = '';
console.log(user.urls.blog == user2.urls.blog); // false
2번째 줄에서 urls 프로퍼티에 copyObject 함수를 실행한 결과를 할당하였다. 이제 urls 내부 프로퍼티의 내부까지 복사해서 새로운 데이터가 만들어졌으므로 5번째 줄(user와 user2의 portfolio)와 8번째 줄(user와 user2의 blog)의 값이 서로 다르다는 것을 알 수 있다.
이제 copyObject
함수를 깊은 복사 방식으로 고친 코드는 다음과 같다.
var copyObjectDeep = function(target) {
var result = {};
if (typeof target === 'object' && target !== null) {
for (var prop in target) {
result[prop] = copyObjectDeep(target[prop]);
}
} else {
result = target;
}
return result;
};
⭐JSON을 활용한 간단한 깊은 복사 코드
객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 방법이다. 하지만 메서드나 숨겨진 프로퍼티와 같이 JSON으로 변경할 수 없는 프로퍼티들은 모두 무시한다는 단점이 있다.
var copyObjectViaJSON = function(target) {
return JSON.parse(JSON.stringify(target));
};
var obj = {
a: 1,
b: {
c: null,
d: [1, 2],
func1: function() {
console.log(3);
},
},
func2: function() {
console.log(4);
},
};
var obj2 = copyObjectViaJSON(obj);
obj2.a = 3;
obj2.b.c = 4;
obj.b.d[1] = 3;
console.log(obj); // { a: 1. b: { c: null, d: [1, 3], func1: f() }, func2: f() }
console.log(obj2); // { a: 3. b: { c: 4, d: [1, 2] } }
1.6. undefined와 null
자바스크립트에는 '없음' 을 나타내는 값이 두 가지가 있다. 바로 undefined
와 null
이다.undefined
는 사용자가 명시적으로 지정할 수도 있지만 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하는 경우도 있다.
- 값을 대입하지 않은 변수를 지정하지 않은 식별자에 접근할 때
- 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
- return 문이 없거나 호출되지 않는 함수의 실행 결과
var a;
console.log(a); // undefined
var obj = { a:1 };
console.log(obj.a); // 1
console.log(obj.b); // undefined
console.log(b); // ReferenceError: b is not defined
var func = function() { };
var c = func(); // return이 없으면 undefined를 반환한 것으로 간주
console.log(c); // undefined
하지만 undefined가 배열에서는 다르게 작동한다. 배열에서는 undefined
를 고유의 키값을 가진 하나의 요소로 여겨서 작동한다.
var arr1 = [];
arr1.length = 3;
console.log(arr1); // [empty x 3]
var arr2 = new Array(3);
console.log(arr2); // [empty x 3]
var arr3 = [undefined, undefined, undefined];
console.log(arr3); // [undefined, undefined, undefined]
이처럼 배열에 세 개의 빈 요소를 확보했지만 그 요소에는 어떤 값도(undefined조차) 할당되지 않았다.
하지만 arr3을 만들 때에는 직접 undefined를 부여하자 결과가 다르게 나오는 것을 확인할 수 있다.
이를 피하기 위해서는 undefined
대신 null
을 사용하면 된다.
동등 연산자 ( == ) 로 null과 undefined를 비교할 경우 같다고 출력한다. 따라서 일치 연산자인 ( === ) 를 사용해야 정확히 판단할 수 있다.
학습에 도움이 될 만한 자료 공유
- immer 라이브러리에 대한 현수님이 정리한 블로그 포스팅
- 민재님이 공유해주신 자바스크립트 웹 에디터
'4-1기 스터디 > Javascript-Typescript Deep Dive' 카테고리의 다른 글
[JS Deep Dive 스터디] 5주차 - 클로저 (0) | 2023.01.25 |
---|---|
[JS Deep Dive 스터디] 4주차 - 콜백 함수 (1) | 2023.01.25 |
[JS Deep Dive 스터디] 3주차 - this (0) | 2023.01.25 |
[JS Deep Dive 스터디] 2주차 - 실행 컨텍스트 (0) | 2022.11.06 |
[JS Deep Dive 스터디] OT (0) | 2022.10.18 |
댓글