CoreJavaScript
자바스크립트의 데이터 타입에는 크게 두가지가 있으며
기본형데이터와 참조형데이터이다.
기본형 : Number, String, Boolean, Null, Undefined, Symbol ...
참조형 : Object, Array, Function, Date, RegExp(정규표현식)
기본형의 특징으로는 불변성(immutability)을 띈다
단순히 변수 a에 15를 담고 다시 20을 담으면 되는데 왜 불변성을 띄는거지? 라고 생각 할 수 있지만 이 부분을 이해하려면 메모리와 데이터에 대한 지식이 필요하고 나아가서 식별자, 변수의 개념을 구분할 수 있어야 한다.
변수는 변할수 있는 데이터이며 식별자는 데이터를 식별하는데 사용하는 이름 즉 변수명이다.
//(1)
var a;
a = 'abc';
var a = 'abc';
//(2)
a = a + 'def';
(1)를 이야기할때에 맨 위에 부터 변수 a를 선언하고 변수 a에 데이터를 할당하며 그 선언과 할당을 합친 문법이 아래라고 생각하면 된다.
그래서 변수 a라는 녀석에 abc라는 데이터가 담긴 주소값을 참조하게 되는 것이다.
이러한 이유는 데이터 변환을 좀 더 자유롭게 할 수 있게 하기 위함이며
동시에 메모리를 더욱 효율적이게 관리하기 위한 고민의 결과이다.
(2)를 보면 새롭게 데이터를 할당하게 되는데 이때에 기존의 abc값을 바꾸는 것이 아닌 새롭게 추가되는 abcdef 라는 녀석의 데이터 블록을 만들어 그 주소 값을 변수 a에 넣게 되는 식이다
이렇기에 기존의 데이터인 abc는 사용이 되지 않지만 변하지 않는 불변성을 가지게 되고 결국 가비지컬렉터에 의해 수거된다.
이렇게 기본형을 불변값으로 설명을 했는데 그렇다면 참조형 데이터는 모두 가변성이 되는 것일까? 그건 아니다
기본적으로 성질은 가변성이지만 설정에 따라 불변값으로 활용하는 경우도 있다.
var obj1 = {
a: 1,
b: 'bbb'
}
위의 코드를 보면 obj1이라는 이름의 변수 공간을 확보하고 그 데이터 공간은 새로운 객체의 변수영역을 만들어 값들의 주소값을 묶어서 가지고 있는 그룹이 된다. 그러면 새로운 객체 변수 영역의 경우에는 a공간 b공간이 생기고 거기에 담기는 데이터는 다시 위로 올라가서 또 다른 데이터 공간을 만들어 각각의 데이터를 담고 그것을 또 참조하는 식이다.
// (1)
var obj1 = {
a: 1,
b: 'bbb'
};
obj1.a = 2;
// (2)
var obj2 = {
x: 3,
arr: [ 3, 4, 5 ]
}
obj.arr = 'str';
(1)에서는 a에 값을 재 할당 하는 것인데 기본형 데이터와 마찬가지로 기존 1이라는 데이터는 그대로 있고 새로운 2라는 데이터영역이 만들어지고 그 주소값을 객체의 변수 영역에 참조하라고 주는 형식이다 결국 새로운 객체가 만들어진 것이 아니라 기존 객체 내부의 값만 바뀌게 된 것.
(2)의 경우엔 참조형 데이터안에 또다른 배열의 참조형 데이터가 있는 상황이다 이럴 경우엔 기존 객체의 변수 영역에서 참조하는 데이터 영역의 경우 이름이 인덱스로 되어 있는 또다른 배열 변수 영역이 생성되고 각 값들은 똑같이 데이터 영역에서 가지고 오게 된다.
그러한 뒤에 새롭게 str로 데이터가 할당 될때는 기존에 사용하던 배열의 변수 영역은 가비지 컬렉터에 의해 수거 되고 새롭게 데이터 영역이 생성되어 담기는 식이다.
얕은 복사와 깊은 복사를 알기전에 알아야 하는 것이 있다.
먼저 기본형과 참조형의 데이터의 큰 차이점은 기본형은 값을 복사하고 참조형은 주소값을 복사한다라고 설명에 나와 있다 이 부분은 주니어 개발자들에게 깊게 설명할 경우 혼란을 줄 수 있기에 있는 설명이며, 정확히는 기본형도 결국 주소값을 참조하게 된다.
또한 참조형 데이터의 경우 아래와 같은 (1)코드에서 보면 이러한 값을 변경 후 복사에 대해 문제가 있다
(1)
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd'}
var obj2 = obj1;
b = 15;
obj2.c = 20;
//결과는 ?
a !== b
obj1 === obj2
(2)
var a = 10;
var b = a;
var obj1 = { c: 10, d: 'ddd'}
var obj2 = obj1;
b = 15;
obj2 = { c: 20, d: 'ddd'}
이러한 문제들을 해결하기 위해 (2)와 같이 아에 새로운 객체를 할당하는 방법들이 있다
하지만 이 부분 또한 수동으로 계속 만들어 주는 것은 문제가 있기에 자동으로 새로운 객체를 만들어 주는 도구(immer.js / immutable.js)등이 있다
(1) 얕은 복사
얕은 복사의 경우 쉽게 바로 아래 단계의 값만 복사하는 방법이다.
var copyObject = function (target) {
var result = {};
for (var prop in target) {
result[prop] = target[prop];
}
return result;
}
위의 코드를 보면 이 copyObject라는 함수를 통해 얕은 복사를 하는 방법이다
이 함수를 통해 객체를 복사하면 무슨 문제인지 아래 코드를 확인해보자.
var user = {
name : "min",
urls : {
portfolio: 'www.naver.com',
blog: 'www.google.com'
}
};
var user2 = copyObject(user);
user2.name = "superman";
console.log(user.name === user2.name); //false
user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
user2.urls.blog = '';
console.log(user.urls.blog === user2.urls.blog); // true
위 코드와 같이 중첩된객체(참조형 데이터 안에 또다른 참조형 데이터가 있을경우)의 경우에는 복사가 되지 않고 기본형 데이터만 복사가 됬다
이 말은 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주소값만 복사한다는 의미이다. 그러면 원본과 참조형이 동일한 데이터를 가르키기 때문에 사본을 바꾸면 원본도 바뀌고 원본을 바꾸면 사본 또한 바뀐다.
또한 얕은 복사 방법으로는 ES6 문법에서 사용 가능한
스프레드 연산자로
var user2 = {...user};
이렇게 복사가 가능하기도 하며 또 다른 방법으로는
Object.assign() 으로도 가능하다.
(2) 깊은 복사
깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법을 말한다.
위의 얕은 복사에서 중첩된 객체를 복사 할 때에 기본형 데이터와 참조형 데이터의 차이를 극복할 수 있는 녀석이다.
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
위의 코드처럼 중첩된 객체인 urls까지 복사해주어 깊은 복사에 대한 기본적인 이해를 나타내는 코드이다.
하지만 중첩된 객체가 많을 수록 수동으로 하기에 아래의 두가지 방법으로도 사용이 가능합니다.
// (1) 깊은 복사를 하게 해주는 함수 제작
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;
};
//(2) JSON을 활용한 깊은 복사
var copyObjectViaJSON = function(target) {
return JSON.parse(JSON.stringify(target));
};
(1)의 방법의 경우 함수를 만들어서 사용하는 것이며 자기 자신을 재귀적으로 호출하고 객체가 아닌 경우에는 target 그대로 저장을 하게 된다
(2)의 방법을 보면 너무나도 간단하다.
원리 또한 단순하며 객체 자체를 JSON 문법으로 표현된 문자열로 바꾸었다가 다시 JSON 객체로 바꾸는 것이다. 이렇게 하게 되면 JSON의 객체같은 경우는 객체 안에 함수나 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 전부 무시하기에 잘 사용 해야한다.
undefined와 null의 경우 간단히 정의만 정리하고 코드를 통해 비교를 확인만 해보자.
undefined가 나오는 경우에는 ?
(1) 값을 대입하지 않은 변수에 접근시에 생긴다
(2) 객체 내부의 존재하지 않는 프로퍼티에 접근시에 생긴다
(3) return 문이 없는 함수를 실행시에 생긴다
undefined는 사용자가 임의로 undefined를 할당을 하지 말고 자바스크립트가 설정해주는 undefined만 사용하는 것이 좋다
null의 경우에는 이제 사용자가 명시적으로 값이 없음을 표현하기 위해 대입한 값이다.
또한 자바스크립트에서 null을 타입으로 비교를 하면 안된다
왜냐하면 자바스크립트의 버그인데 null을 typeof로 찍어 봤을때에 Object가 나오기 때문이다
이제 아래의 코드로 undefined와 null의 비교나 할당이 되어 있을때에 함수 실행시 결과 값으로 알아보고 마무리 하자.
// 빈 요소와 undefined를 할당하였을때의 배열 순회의 차이
var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;
arr1.forEach((v, i) => {console.log(v, i)}); // undefined 0, 1 1
arr2.forEach((v, i) => {console.log(v, i)}); // 1 1
arr1.map((v, i) => {console.log(v, i)}); // undefined 0, 1 1, [undefined, undefined]
arr2.map((v, i) => {console.log(v, i)}); // 1 1, [empty, 2]
arr1.filter((v) => {return !v;}); // [undefined]
arr2.filter((v) => {return !v;}); // []
arr1.reduce((p, c, i) => {return p + c + i}, ''); // undefined011
arr2.reduce((p, c, i) => {return p + c + i}, ''); // 11
// ===========================
//undefined와 null의 비교
var n = null;
console.log(typeof n); // object
console.log(n == undefined); // true
console.log(n == null); // true
console.log(n === undefined); // false
console.log(n === null); // true
모든 내용은 코어자바스크립트 공부를 하며 책 내용을 발췌해 이해한 내용들만 추려 적은 내용입니다 해당 코드들은 책 내용의 코드의 값들을 변경하거나 이해를 위해 그대로 가지고 왔습니다.
이 남자 제대로 알고싶다..