[모던 자바스크립트 Deep Dive] 09 ~ 11장
09장 타입 변환과 단축 평가
1. 타입 변환
1) 명시적 타입 변환(explicit coercion) / 타입 캐스팅
: 개발자가 의도적으로 값의 타입을 변환 하는 것
var x = 10;
var str = x.toString(); // 숫자를 문자열로 타입 캐스팅
2) 암묵적 타입 변환
: 개발자의 의도와는 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동 변환되는 것
var x = 10;
var str = x + ''; // typeof str = string
👉 명시적/암묵적 타입 변환은 기존 원시 값을 직접 변경하는 것이 아니라, 새로운 원시 값을 생성하는 것!
2. 암묵적 타입 변환
1) 문자열 타입으로 변환
1 + '2' // "12"
NaN + '' // "NaN"
true + '' // "true"
null + '' // "null"
(Symbol()) + '' // Error
({}) + '' // "[object Object]"
[] + '' // ""
[10, 20] + '' // "10,20"
(function(){}) + '' // "function(){}"
Array + '' // "function Array() { [native colde] }"
➕(더하기)연산자의 피연산자 중 하나 이상이 문자열일 시 자동 형변환
2) 숫자 타입으로 변환
1 - '1' // 0
1 * '10' // 10
1 / 'one' // NaN
➕(더하기)연산자를 제외한 산술 연산자는 피연산자를 암묵적으로 숫자 타입으로 변환
만약, 숫자 타입으로 변환할 수 없는 경우는 평가 결과가 NaN이 됨
'1' > 0 // true
+'' // 0
+'0' // 0
+'1' // 1
+true // 1
+false // 0
+null // 0
+undefined // NaN
+Symbol() // Error
+{} // NaN
+[] // 0
+[10, 20] //NaN
+(function(){}) // NaN
빈 문자열, 빈 배열, null, false는 0으로 치환, true는 1로 변환됨
3) 불리언 타입으로 변환
- 자바스크립트 엔진은 조건식의 평가 결과를 불리언 타입으로 항상 암묵적 타입변환한다.
- 불리언 타입이 아닌 값은 Truthy 또는 Falsy 값으로 구분한다.
📌 Falsy 값 : false, undefined, null, 0, -0, NaN, 빈 문자열
3. 명시적 타입 변환
1) 문자열 타입으로 변환
- String 생성자 함수를 new 연산자 없이 호출하는 방법
- Object.prototype.toString 메서드를 이용하는 방법
- 문자열 연결 연산자를 이용하는 방법
String(true); // "true"
(true).toString() // "true"
true + ''; // "true"
2) 숫자 타입으로 변환
- Number 생성자 함수를 new 연산자 없이 호출하는 방법
- parseInt, parseFloat 함수를 이용하는 방법(문자열만 가능)
- + 단항 산술 연산자를 이용하는 방법
- * 산술 연산자를 이용하는 방법
Number('false'); // 0
parseInt('0'); // 0
parseInt(false); // NaN
+'0' // 0
'0' * 1; // 0
3) 불리언 타입으로 변환
- Boolean 생성자 함수를 new 연산자 없이 호출하는 방법
- !! (부정 연산자 두번) 을 이용하는 방법
Boolean(''); // false
!!''; // false
!!NaN; // false
!!Infinity; // true
!!null; // false
!!undefiend; // false
!!{}; // true
!![]; // true
📌 Infinity, 빈 객체, 빈 배열은 true 값이다 !
4. 단축 평가
1) 논리 연산자를 사용한 단축 평가
: 표현식을 평가하는 도중 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것
📌 &&, || 연산자는 논리 연산의 결과를 결정하는 피연산자를 타입변환하지 않고 그대로 반환
단축 평가 표현식 | 평가 결과 |
true || anything | true |
false || anything | anything |
true && anything | anything |
false && anything | false |
- 👉 사용하는 경우
- 객체라고 예상하는 변수가 null / undefined가 아닌지 확인하고 프로퍼티를 참조할 때
- 함수 매개변수에 기본 값을 설정할 때
2) 옵셔널 체이닝 연산자
- es11에서 도입된 옵셔널 체이닝 연산자는 좌항의 피연산자가 null / undefined 인 경우 undefiend를 반환하고,
그렇지 않으면 우항의 프로퍼티를 참조
var elem = null;
var value1 = elem?.value; // undefined
var value2 = elem && elem.value; //null
- 논리 연산자 &&가 좌항 피연산자가 Falsy 값이 면 좌항 피연산자를 그대로 반환하는 경우에 반면,
옵셔널 체이닝 연산자는 null 또는 undefined가 아니면 우항의 프로퍼티 참조를 이어간다
var str = '';
var length = str && str.length; // ''
var length = str?.length; // 0
3) null 병합 연산자
- es11에서 도입된 null 병합연산자 ?? 는 좌항의 피연산자가 null 또는 undefiend인 경우 우항의 피연산자를 반환하고,
그렇지 않으면 좌항의 피연산자를 반환함 (변수의 기본값을 설정할 때 유용)
var foo = null ?? 'default string'; // 'default string'
var foo = '' || 'default string'; // 'default string'
var foo = '' ?? 'default string'; // ''
|| 연산자와의 차이점은, 좌항의 피연산자 값이 Fasly 값이라도 null / undefined가 아니면 좌항의 피연산자 값을 반환
(기본값이 0, 빈 문자열으로도 유효하다면 필요하다)
10장 객체 리터럴
1. 객체란?
- 자바스크립트는 객체 기반 프로그래밍 언어, 원시 값을 제외한 나머지는 "객체"로 구성되어 있음
- 객체는 원시 값과 다르게 변경 가능한 값이다.
- 객체는 0개 이상의 프로퍼티로 구성된 집합, 키와 값으로 구성된다.
✅ 프로퍼티와 메서드
- 프로퍼티 : 객체의 상태를 나타내는 값
- 메서드: 프로퍼티를 참조하고 동작할 수 있는 동작 (프로퍼티 값이 함수 일 때)
2. 객체 리터럴에 의한 객체 생성
1) 생성 방법들
- 객체 리터럴
- Object 생성자 함수
- 생성자 함수
- Object.create 메서드
- 클래스(es6)
2) 리터럴을 이용한 생성 방법
- 중괄호 내에 0 개 이상의 프로퍼티를 정의
var person = {
name: 'Kim',
sayHello: function () {
console.log(`hello! ${this.name}`);
}
}
3. 프로퍼티
- 객체는 프로퍼티의 집합이며 프로퍼티는 키과 값으로 구성된다.
- 프로퍼티 키 : 빈 문자열을 포함하는 모든 문자열 또는 심벌 값
- 예약어를 프로퍼티 키로 사용 가능하다 (권장 x)
- 프로퍼티 키를 중복 선언 하면 나중에 선언한 프로퍼티 키 값이 앞서 선언 한 값을 덮어쓴다.
- 프로퍼티 값: 자바스크립트에서 사용할 수 있는 모든 값
* 프로퍼티 키값이 식별자 네이밍 규칙을 따를 때는 따옴표를 생략할 수 있다.
var person = {
firstName : 'kim', // 네이밍 규칙 준수
'last-name' : 'euna' // 네이밍 규칙 준수 x
}
✅ 동적 키 값 생성 하기
- 프로퍼티 키를 사용할 표현식을 대괄호로 묶어야 한다.
var obj = {};
var key= 'hello';
obj[key] = 'world';
4. 프로퍼티 접근
1) 접근 방법
- 마침표 프로퍼티 접근 연산자를 사용하는 마침표 표기법
- 대괄호 프로퍼티 접근 연산자를 사용하는 대괄호 표기법
var person = {
name: 'Kim'
};
console.log(person.name);
console.log(person['name']); // 반드시 따옴표로 감싸야 함 아니면 식별자로 인식
📌 객체에 존재하지 않는 프로퍼티에 접근하면 undefined를 반환 !!!!
프로퍼티 키가 식별자 네이밍 규칙을 준수하지 않는 이름일 때, 대괄호 표기법을 이용 !
단, 숫자로 이루어진 키면 따옴표를 생략할 수있다.
➕ 주의 할 점
perosn.last-name; // 브라우저 환경 : NaN
// Node.js 환경: Error
person.last값이 없으므로 undefiend로 평가, undefined-name 이면 name이라는 식별자를 찾는데,
브라우저 환경에서는 name이 window를 가르킨다(기본 값 빈 문자열) 따라서 undefiend-'' => NaN 값이다.
5. 프로퍼티 삭제
- delete 연산자를 이용한다
var person = {
name: 'Lee'
};
person.age = 20; // 프로퍼티 동적 생성
delete person.age; // 프로퍼티 삭제
* 존재하지 않는 프로퍼티를 삭제해도 에러는 안난다.
6. 객체 리터럴 확장 기능
1) 프로퍼티 값으로 변수를 사용하는 경우, 변수 이름과 프로퍼티 키가 동일한 이름일 때 프로퍼티 키 생략 가능
let x = 1, y = 2;
const obj = { x, y };
2) 계산된 프로퍼티 이름
- 값으로 변환 되는 표현식을 사용해 프로퍼티 키를 동적으로 생성할 수 있다.
var x = 'abc';
var i = 0;
var obj = {};
obj[x + '-' + ++i] = i; // { abc-1: 1 }
obj[`${x}-${++i}`] = i;
const obj2 = {
[`${x}-${++i}`]: i
}
3) 메서드 축약 표현
var obj = {
sayHi: function() {},
sayHello() {} // es6
};
es6의 메서드 축약 표현으로 정의한 메서드는 프로퍼테 할당한 함수와 다르게 동작한다.
11장 원시 값과 객체의 비교
1. 원시 값
- 변경 불가능한 값
- 원시 값을 변수에 할당하면 변수에는 실제 값이 저장된다.
- 불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법은 없다.
(재할당시 새로운 메모리 공간을 확보하고 변수가 참조하던 메모리 공간의 주소를 변경한다)
1) 문자열과 불변성
- 원시 타입 별로 메모리 공간의 크기가 미리 정해져 있다
- 문자열 타입 : 2byte(1개의 문자)
- 숫자 타입 : 8byte
- 자바스크립트는 개발자의 편의를 위해 문자열 타입을 제공한다.
- 문자열 또한 원시 값이며, 생성 이후엔 변경 불가능하다.
var str = 'string';
str[0] = 'S'; // 배열처럼 접근 가능하다
console.log(str); // string (변경되지 않음)
2. 값에 의한 전달
var score = 80;
var copy = score;
score = 100;
console.log(score); // 100
console.log(copy); // 80
변수에 변수를 할당했을 때는, 변수의 값이 새로운 변수 값으로 생성되어 할당된다.
(copy에 score를 할당했을 때, 80이라는 값이 새롭게 생성되어 copy 변수에 할당된다)
👉 값에 의한 전달
따라서 score 변수와 copy 변수에 들어간 80 은 다른 메모리 공간에 저장된 별개의 값이다.
- 사실 자바스크립트에서 "값에 의한 전달" 이라는 용어는 없다.
변수에는 값이 전달되는게 아니라 메모리 주소가 전달되기 때문에 식별자는 값이 아니라 메모리 주소를 기억한다.
var x = 10; // 할당 연산자에 의해 x라는 식별자는 10이 저장된 메모리 주소가 할당된다.
아래 예제는 두가지로 해석이 가능하다.
var copy = score;
- 새로운 80을 생성(복사)해서 메모리 주소를 전달하는 방식(두 변수의 메모리 주소가 다르다)
- score 변수값 80의 메모리 주소를 그대로 전달하는 방식(두 변수의 메모리 주소가 같다)
3. 객체
- 자바스크립트 객체는 프로퍼티 키를 인덱스로 사용하는 해시테이블과 비슷한 형태로 구현되어있다.
- 자바/C++ 처럼 클래스 기반으로 객체를 생성하는 반면, 자바스크립트는 클래스 없이 객체 생성이 가능하다.
따라서 동적으로 프로퍼티나 메서드를 추가할 수 있다는 장점이 있지만 성능면에서는 비효율적이다.
* V8 자바스크립트 엔진에서는 프로퍼티에 접근하기 위해 동적 탐색 대신 히든 클래스를 사용해 성능을 개선했다.
1) 변경 가능한 값
- 객체 타입의 값은 변경 가능한 값 !
- 객체를 변수에 할당하면, 변수는 실제 객체가 저장되어 있는 메모리 주소를 참조 값을 가르킨다.
➕ 원시값을 변수에 할당하면 "변수는 O값을 갖는다/O 값이다" 라고 표현하는 대신,
객체를 할당한 변수는 변수는 "객체를 참조하고 있다/가르키고 있다" 라고 표현한다
👉 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다 !
- 이런 객체의 구조적 설계는 여러개의 식별자가 하나의 객체를 공유할 수 있다는 부작용이 있다.
2) 얕은 복사 vs 깊은 복사 ⭐⭐⭐⭐⭐⭐
- 얕은 복사 : 참조 값을 복사 (객체 안에 객체가 있을 경우 한 개의 객체라도 기존 변수의 객체를 참조하고 있다면 이를 얕은 복사라고 합니다)
- 깊은 복사 : 객체의 실제 값 복사 (객체에 중첩되어 있는 객체까지 복사)
const obj = { x: { y: 1 } };
// 얕은 복사
const c1= { ...obj };
console.log(c1 === o); // false
console.log(c1.x === o.x); // true
위 예제에서는 obj의 x 객체와 c1의 x 객체가 같은 참조값을 갖고 있기에 얕은 복사라고 한다.
이 외에 Array.prototype.slice() 메서드, Object.assgin() 메서드도 얕은 복사다.
* 깊은 복사 방법
- lodash의 cloneDeep() 메서드 사용
- JSON.parse, JSON.stringfy 이용
- 재귀 함수를 이용한 복사 방법
3) 참조에 의한 전달
var person = {}
var copy = person;
위 예제에서는 person과 copy가 하나의 객체를 공유한다 (같은 메모리 주소값을 참조한다)
따라서 하나라도 변경하면 다른 것도 같이 변경된다.
👉 "값에 의한 전달" 과 "참조에 의한 전달" ?
자바스크립트에서는 "참조에 의한 전달" 은 존재 하지 않는다.
모두 값에 의한 전달 방식이고, 차이는 변수에 저장되어 있는 값이 원시 값인지, 참조 값인지에 따라 달라진다
* 공유에 의한 전달이라고 표현하기도 한다.