자바스크립트의 모든 값은 타입이 있다. 값의 타입은 다른 타입으로 개발자에 의해 의도적으로 변환할 수 있다. 또는 자바스크립트 엔진에 의해 암묵적으로 자동 변환될 수 있다.
전자와 같은 경우를 타입 캐스팅(Type casting) 혹은 명시적 타입 변환이라 하고, 후자와 같은 경우를 타입 강제 변환(Type coercion) 혹은 암묵적 타입 변환이라고 한다.
- toString(), toNumber(), toBoolean()
if (1) { }
0 + '' // "0"
vs(0).toString // "0"
조건식에서 포괄적으로 나타내기 위해 사용!
여기서 Falsy 값으로 분류되는 걸 외우고 여집합을 Truthy값이라고 생각하면 외우기 편하다.
Falsy 값
- false
- undefined
- null
- 0, -0
- NaN
- ’’ (빈문자열) (빈 배열 혹은 빈 객체은 트루시값)
자바스크립트는 동적 타입(dynamic typed) 언어이므로 변수에 어떤 값이 할당될 지 예측하기 어렵다. 변수나 반환값의 타입을 사전에 지정하지 않는 자바스크립트의 동적 타이핑(Dynamic Typing)에 의해 자바스크립트는 타입 체크가 필요하다.
Object.prototype.toString
를 사용하여 객체의 종류(일반 객체, 배열, Date, RegExp, Function, DOM 요소 등)까지 식별할 수 있다.
Java, C++과 같은 클래스 기반 객체지향 프로그래밍 언어와 달리 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어이다. 따라서 자바스크립트의 동작 원리를 이해하기 위해서는 프로토타입의 개념을 잘 이해하고 있어야 한다.
프로토타입 기반 객체지향 프로그래밍 언어는 클래스 없이도 객체를 생성할 수 있다.
자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있다. 그리고 이것은 마치 객체 지향의 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 한다. 이러한 부모 객체를 Prototype(프로토타입) 객체 또는 줄여서 Prototype(프로토타입)이라 한다.
cf) 객체 리터럴 방식이나 생성자 함수 방식이나 결국은 모든 객체의 부모 객체인 Object.prototype
객체에서 프로토타입 체인이 끝나기 때문이다. 이때 Object.prototype
객체를 프로토타입 체인의 종점(End of prototype chain)이라 한다.
[[Prototype]]
vsprototype
프로퍼티
스코프(Scope)는 참조 대상 식별자(identifier)를 찾아내기 위한 규칙이다. 자바스크립트는 이 규칙대로 식별자를 찾는다.
프로그래밍은 변수를 선언하고 값을 할당하며 변수를 참조하는 기본적인 기능을 제공하며 이것으로 프로그램의 상태를 관리할 수 있다. 변수는 전역 또는 지역(코드 블록(if, for, while, try/catch 등))이나 함수 내에 선언하며 코드 블록이나 함수는 중첩될 수 있다. 식별자는 자신이 어디에서 선언됐는지에 의해 자신이 유효한(다른 코드가 자신을 참조할 수 있는) 범위를 갖는다.
스코프
- 전역 스코프 (Global Scope) : 코드 어디에서든 참조 가능
- 지역 스코프 (Local Scope) : 함수 코드 블록 내에서 참조 가능
- 동적 스코프 : 함수가 호출되는 시점에 결정
- 정적 스코프 : 함수가 정의되는 시점에 결정 (===렉시컬 스코프)
변수
- 전역 변수 (Global variable)
- 지역 변수 (Local variable)
전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고, 지역(자바스크립트의 경우 함수 내부)에서 선언된 변수는 지역 스코프를 갖는 지역 변수가 된다.
자바스크립트는 함수 레벨 스코프(function-level scope)를 따른다. 함수 레벨 스코프란 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다.
단, ECMAScript 6에서 도입된 let
키워드와 const
키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다.
자바스크립트에서 함수가 생성되면 본인의 내부 슬롯에 상위 스코프에 대한 참조를 저장한다.
수정이 더 필요하다.
개발자의 의도와는 상관없이 자바스크립트 엔진이 생성한 암묵적 전역 변수는 오류를 발생시키는 원인이 될 가능성이 크다. 따라서 반드시 var, let, const 키워드를 사용하여 변수를 선언한 다음 변수를 사용해야 한다.
오타나 문법 지식의 미비로 인한 실수는 언제나 발생하는 것이다. 따라서 오류를 줄여 안정적인 코드를 생산하기 위해서는 근본적인 접근이 필요하다. 즉, 잠재적인 오류를 발생시키기 어려운 개발 환경을 만들고 그 환경에서 개발을 하는 것이 보다 근본적인 해결책이라고 할 수 있다. 그래서 ES5부터 strict mode가 추가되었다.
'use strict'
라고 입력하면 된다. 하지만 전역의 선두에 추가하면 스크립트 전체에 strict mode가 적용된다. strict mode는 즉시 실행 함수로 감싼 스크립트 단위로 적용하는 것이 바람직하다.
(function () {
let a = 10;
function f1() {
'use strict'; //strict mode
let = 20; //SyntaxError: Unexpected strict mode reserved word
}
f1();
}()); //즉시 실행 함수
- 함수 호출
- 메소드 호출
- 생성자 함수 호출
- apply/ call/ bind 호출
this 구분
- 일반 함수는 호출 위치에 따라 this가 정의된다.
- 화살표 함수는 자신이 선언된 함수 범위에서 this가 정의된다.
const a = {
name: 'Song',
normal: function() {
console.log(this.name);
},
arrow: () => {
console.log(this.name);
}
a.normal(); //Song
a.arrow(); //undefined , 화살표 함수 자신이 선언된 함수 범위에서는 name이 정의되어있지 않다.
const you = {
name: 'You',
normal: a.normal, // '()'는 호출 표시이다. 여기서는 호출하지 않고 할당하고 있다.
arrow: a.arrow
}
you.normal(); // Song , 호출 위치에서 name이 초기화되어 있으니 잘 출력할 수 있다.
you.arrow(); // undefined
constructor
생성자 함수를 클래스 버전으로 바꿔보자.
class Person {
constructor: function (name, age) {
this.name = name;
this.age = age;
}
getYourId() {
return `${this.name}, ${this.age}`
}
classList
vs className
classList.toggle(토큰)
: 토큰을 toggle한다. 토큰이 존재한다면 토큰을 제거하고, 존재하지 않는다면 토큰을 추가한다. class Vehicle {
constructor(name, wheel) {
this.name= name
this.wheel= wheel
}
}
const myVehicle = new Vehicle('운송수단', 2);
class Bicycle extends Vehicle { //확장(상속)
constructor(name, wheel) {
super(name, wheel) //부모의 매개 변수 사용
}
}
const myBycycle = new Bicycle('붕붕이', 2);
class Car extends Vehicle {
constructor(name, wheel, license) {
super(name, wheel)
this.license = license //라이센스라는 속성도 추가할 수 있다.
}
}
const myCar = new Car('현대', 4, true);
- 원시 데이터: String, Number, Boolean, undefined, null
- 참조형 데이터: Object, Array, Function
Immutability(변경불가성)는 객체가 생성된 이후 그 상태를 변경할 수 없는 디자인 패턴을 의미한다.
원시 데이터는 불변하기 때문에 데이터의 생김새가 똑같다면 똑같다고 봐도 되지만, 참조형 데이터는 선언할 때마다 새로운 메모리 주소에 할당되기 때문에 다를 수 있다. 한 쪽은 수정하면 다른 한 쪽도 수정될 수 있다.
(참조형 데이터를 복사)
얕은 복사 (Shallow copy)
, 깊은 복사 (Deep copy)
원시 데이터는 값을 복사 할 때 복사된 값을 다른 메모리에 할당 하기 때문에 원래의 값과 복사된 값이 서로에게 영향을 미치지 않는다. 반면에 참조 데이터는 변수가 객체의 주소를 가리키는 값이기 때문에 복사된 값(주소)이 같은 값을 가리킨다.
얕은 복사는 원래 값과 복사된 값이 같은 참조를 가리키고있는 것을 말한다. 객체 안에 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은 복사라고 한다.
깊은 복사된 객체는 객체안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다. 아예 새로운 메모리 주소를 할당하고 있다.
(cf. lodash
의 .cloneDeep
)