원시자료형(primitive data type) : 고정된 저장 공간을 차지하는 데이터
참조 자료형(reference data type) : 대량의 데이터를 다루기에 적합
: 객체가 아니면서 method를 가지지 않는 6 가지의 타입
string, number, boolean, undefined, bigint, symbol, (null)
- 원시 자료형이 할당될 때는 변수에 값(value) 자체가 담긴다.
- 변수에는 데이터의 크기와 관계없이 데이터는 하나의 정보를 담는다.
- 원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달된다. ( 기존 데이터 영향 x )
- 값 자체에 대한 변경이 불가능(immutable)하지만, 변수에 다른 데이터 할당하는 것은 가능하다. => 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다.
: 원시 자료형이 아닌 모든 것
array[], object{}, function(){}
- 참조 자료형이 할당될 때는 메모리 공간의 주소(reference)값 이 담긴다.
- 하나의 데이터가 아닌 여러 데이터를 담는다.
- 참조 값을 갖는 변수를 다른 변수에 할당하면 주소값이 복사되어 전달된다.
- 기존에 고정된 크기의 저장 공간이 아닌, 동적으로 크기가 변하는 특별한 저장 공간(heap)을 사용한다.
- 주소를 복사(공유)하여 복사한 데이터에서 원소를 변경하면, 주소 안에 있는 데이터도 변경된다. (기존 데이터 영향 o)
- 참조 자료형은 값 자체에 대한 변경이 가능한 값(mutable value)이다.
스코프(Scope) : 변수의 유효범위
- 변수 접근 범위
let username = 'User';
if (username) {
let message = `Hello, ${username}!`;
console.log(message); // Hello, User!
}
console.log(message); // ReferenceError
블록 : 중괄호 안쪽에 변수가 선언되었는지, 바깥에 변수가 선언되었는지
let greeting = 'Hi';
function greetSomeone() {
let firstName = 'User';
return greeting + ' ' + firstName;
}
console.log(greetSomeone()); // Hi User
console.log(message); // ReferenceError
함수 : 함수 안에서 변수가 선언되었는지, 바깥에 변수가 선언되었는지
- 변수 접근 규칙에 따른 유효 범위
안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능하다.
전역 스코프(Global scope) : 가장 바깥의 스코프 <-> 지역 스코프(Local scope)
지역 변수는 전역 변수보다 더 높은 우선 순위를 가진다.
스코프는 중첩이 가능하다.
- 스코프의 종류
블록 스코프 (block scope)
중괄호({}
)로 둘러싼 범위
화살표 함수( => {}
)로 둘러싼 범위
( 같은 함수여도 화살표 함수를 사용하면 블록 스코프로 취급 )
함수 스코프 (function scope)
function{}
)로 둘러싼 범위- 변수를 정의하는 방법
var 키워드
블록 스코프를 무시하고, 함수 스코프를 따른다.
( 화살표 함수의 블록 스코프는 무시하지 않는다.)
재선언을 해도 에러를 내지 않는다.
let 키워드
블록 스코프를 따르며, 재선언을 방지한다.
const 키워드
블록 스코프를 따르며, 재할당이 불가능하다.
변하지 않는 값, 상수(constant)를 정의할 때 사용한다.
값의 변경을 최소화하여 보다 안전하다.
( 값을 새롭게 할당할 일이 없다면, const 키워드 사용 권장한다. )
var 보다 let, const 키워드를 사용하는 것이 좋다.
선언 키워드(var, let, const) 없이 변수를 할당하지 말아야 한다.
=> 선언 없이 변수를 할당하면, 해당 변수는 var로 선언한 전역 변수처럼 취급된다.
Strict Mode : 문법적으로 실수할 수 있는 부분들을 에러로 판단한다.
'use strict'
: js 파일 상단에 코드를 적어주면 안전하게 코드 작성 가능하다.
window 객체 ( 브라우저에만 존재 )
: 브라우저의 창(window)을 대표하는 객체 + 전역 영역을 담고있다.
함수 선언식으로 함수를 선언하거나 var로 전역 변수를 만들면, window 객체에서도 동일한 값을 찾을 수 있다.
전역 변수 : 어디서든 접근 가능한 변수
- 부수 효과(side effect) : 로직에 의해 의도되지 않은 변경 발생 가능하므로 전역 변수를 최소화하는 것이 좋다.
함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말하며,
이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
( 클로저의 함수는 어디에서 호출되느냐와 무관하게 선언된 함수 주변 환경에 따라 접근할 수 있는 변수가 정해진다. )
=> 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수이다.
const adder = x => y => x + y; // 함수의 호출 두 번 발생
adder(5)(7); // 12
typeof adder(5); // 'function' => 리턴값이 함수의 형태
adder(5); // y => x + y => 리턴값이 함수의 형태
const adder = x => y => x + y;
// 위와 동일하게 작동하는 코드
const adder = function (x) {
return function (y) { // => 리턴값이 함수의 형태
return x + y;
}
}
리턴하는 함수에 따라 스코프(변수의 접근 범위)가 구분된다.
const adder = function (x) {
return function (y) {
return x + y;
}
}
=> 내부 함수는 외부 함수에 선언된 변수에 접근이 가능하다. ( 그 반대는 x )
외부 함수의 실행이 끝나더라도, 외부 함수 내 변수를 사용할 수 있다.
( 클로저 함수는 어휘적 환경을 메모리에 저장하기 때문 )
=> 함수 내에 데이터를 보존해두고 사용 가능하다.
const add = function (x) {
return function (y) {
return x + y;
}
}
const add5 = add(5); // 함수 실행이 끝나도 5라는 값 사용 가능
add5(5); // 10
add5(10); // 15
클로저 모듈 패턴
내부 함수를 하나만 반환하지 않고, 객체에 담아 여러 개의 내부 함수를 반환하도록 한다.
const counter = () => {
let value = 0;
return { // increase, decrease, getValue 내부 함수를 여러 개 만들 수 있다
increase: () => {
value += 1
},
decrease: () => {
value -= 1
},
getValue: () => value
}
}
const counter1 = counter();
counter1 // {increase: ƒ, decrease: ƒ, getValue: ƒ}
=> counter를 실행해 변수에 담아서 counter 함수는 increase, decrease, getValue 메서드를 포함한 객체 하나를 반환한다.
정보의 접근 제한 (캡슐화)
: "외부 스코프에서 내부 스코프의 변수에 접근할 수 없다"는 규칙 때문에 값을 직접 수정하는 것 불가능 ( 대신 반환하는 객체가 제공하는 메서드를 통해 값을 간접적으로 조작 )
클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.
: 함수 재사용성을 극대화하여 함수 하나를 독립적인 부품의 형태로 분리하는 것
( 클로저를 통해 데이터와 메서드를 같이 묶어서 다룰 수 있다. )
재활용 가능한 함수
// 위 코드 작성되어 있다고 가정하고 아래 코드 작성
const counter1 = counter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); // 1
const counter2 = counter();
counter2.decrease();
counter2.decrease();
counter2.increase();
counter2.getValue(); // -1
counter1 과 counter2의 value는 서로 영향을 주지 않고, 각각의 값 보존 가능
+ 모듈 : 하나의 기능을 온전히 수행하기 위한 모든 코드를 가지고 있는 코드 모음 ( 다른 모듈에 의존적이지 않고 독립적이어야 함 )
=> 특정 데이터를 다른 코드의 실행으로부터 보호해야할 때 용이함
ECMA Script: Ecma International이 ECMA-262 기술 규격에 따라 정의하고 있는 표준화된 스크립트 프로그래밍 언어, 자바스크립트를 표준화하기 위해 만들어졌다.
...
): 대상을 개별 요소로 분리, 대상은 이터러블(문자열, 배열...)이어야 한다.
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
sum(...numbers) // 6
: 함수의 마지막 매개변수 앞에 ...
를 붙여 사용한다. 반드시 함수 정의의 마지막 매개변수여야 한다.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
sum(1,2,3) // 6
sum(1,2,3,4) // 10
let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
console.log(lyrics); // ['head', 'shoulders', 'knees', 'and', 'toes']
spread 문법은 기존 배열을 변경하지 않으므로(immutable), arr1의 값을 바꾸려면 새롭게 할당해야 합니다.
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
console.log(arr1); // [0, 1, 2, 3, 4, 5]
let arr = [1, 2, 3];
let arr2 = [...arr]; // arr.slice() 와 유사
arr2.push(4);
console.log(arr); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 };
let mergedObj = { ...obj1, ...obj2 };
console.log(clonedObj); // {foo: 'bar', x: 42}
console.log(mergedObj); // {foo: 'baz', x: 42, y: 13}
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a); // a one
console.log("b", b); // b two
console.log("manyMoreArgs", manyMoreArgs); // manyMoreArgs ['three', 'four', 'five', 'six']
}
myFun("one", "two", "three", "four", "five", "six");
spread 문법을 이용하여 값을 해체한 후(배열이나 객체의 속성을 해체하여), 개별 값을 변수에 담을 수 있게 하는 JavaScript 표현식
const [a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [ 30, 40, 50 ]
const
, let
, var
)과 함께 사용하지 않으면 에러 발생할 수 있다.const { a, b, ...rest } = { a: 10, b: 20, c: 30, d: 40 };
console.log(a); // 10
console.log(b); // 20
console.log(rest); // { c: 30, d: 40 }
+ 선언 없는 할당
: 구조 분해를 통해 변수의 선언과 분리하여 변수에 값을 할당할 수 있다.
var a, b;
({a, b} = {a: 1, b: 2});
console.log(a); // 1
console.log(b); // 2
- 함수에서 객체 분해
function whois({ displayName: displayName, fullName: { firstName: name } }) {
console.log(displayName + " is " + name); // jdoe is John
}
let user = {
id: 42,
displayName: "jdoe",
fullName: {
firstName: "John",
lastName: "Doe",
},
};
whois(user);
: 함수표현식으로 함수 정의할 때, function
키워드 대신 화살표(=>
)를 사용한다.
=> 함수표현식으로 함수를 정의할 때보다 간략하게 함수를 정의할 수 있다.
// 아래 코드 모두 동일하게 동작
const arrow = x => { return x + x }
const arrow = (x) => { return x + x }
return
키워드 생략 가능)// 아래 코드 모두 동일하게 동작
const arrow = x => x + x
const arrow => x => { return x + x }
const arrow = function (x) {
return x + x;
}