재귀 함수 : 자기 자신을 호출하여 실행하는 함수.
function pow(x, n) {
return (n == 1) ? x : (x * pow(x, n - 1));
}
사용 예)
let company = { // 동일한 객체(간결성을 위해 약간 압축함)
sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }],
development: {
sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }],
internals: [{name: 'Jack', salary: 1300}]
}
};
// 급여 합계를 구해주는 함수
function sumSalaries(department) {
if (Array.isArray(department)) { // 첫 번째 경우
return department.reduce((prev, current) => prev + current.salary, 0); // 배열의 요소를 합함
} else { // 두 번째 경우
let sum = 0;
for (let subdep of Object.values(department)) {
sum += sumSalaries(subdep); // 재귀 호출로 각 하위 부서 임직원의 급여 총합을 구함
}
return sum;
}
}
alert(sumSalaries(company)); // 7700
재귀를 사용하면 구현과 유지보수가 쉽다는 장점이 있어 많이 사용된다.
연결리스트(linked list)
배열의 끝에 하는 연산인 arr.push/pop에 비해 앞쪽 요소에 하는 연산은 느리다.
빠르게 삽입 혹은 삭제를 해야 할 때는 배열대신 연결 리스트를 사용하는 것이 더 빠르다.
- value와 next 프로퍼티를 조합해 정의할 수 있음.
나머지 매개변수 ...
값을 담을 배열 이름을 마침표 세 개 뒤에 붙여주면, 남은 매개변수 모두를 해당 배열 이름 안에 담아준다.
단, 나머지 매개변수는 항상 마지막에 있어야 한다.
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Bora Lee
// 나머지 인수들은 배열 titles의 요소가 됩니다.
// titles = ["Software Engineer", "Researcher"]
alert( titles[0] ); // Software Engineer
alert( titles[1] ); // Researcher
alert( titles.length ); // 2
}
showName("Bora", "Lee", "Software Engineer", "Researcher");
argument 객체
argument object == array-like object
나머지 매개변수가 생기기 전에 사용했던 문법.
스프레드 문법
배열을 통째로 매개변수에 넘겨줄 때, '...arr'형식으로 확장시키는 것.
스프레드 문법을 사용하면 문자열도 이터레이터를 통해 자동으로 문자 배열로 변환시켜 준다.
//스프레드 문법 사용
let arr = [3, 5, 1];
let arr2 = [8, 9, 15];
let merged = [0, ...arr, 2, ...arr2];
alert(merged); // 0,3,5,1,2,8,9,15 (0, arr, 2, arr2 순서로 합쳐집니다.)
//--문자열 -> 문자 배열
let str = "Hello";
// Array.from은 이터러블을 배열로 바꿔줍니다.
alert( Array.from(str) ); // H,e,l,l,o
배열과 객체의 복사본 만들기
참조에 의한 객체 복사와 같이 Object.assign() 말고 스프레드 문법을 사용해 배열과 객체 복사 가능.
배열 복사
let arr = [1, 2, 3];
let arrCopy = [...arr]; // 배열을 펼쳐서 각 요소를 분리후, 매개변수 목록으로 만든 다음에
// 매개변수 목록을 새로운 배열에 할당함
// 배열 복사본의 요소가 기존 배열 요소와 진짜 같을까요?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
// 두 배열은 같을까요?
alert(arr === arrCopy); // false (참조가 다름)
// 참조가 다르므로 기존 배열을 수정해도 복사본은 영향을 받지 않습니다.
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3
객체 복사
let obj = { a: 1, b: 2, c: 3 };
let objCopy = { ...obj }; // 객체를 펼쳐서 각 요소를 분리후, 매개변수 목록으로 만든 다음에
// 매개변수 목록을 새로운 객체에 할당함
// 객체 복사본의 프로퍼티들이 기존 객체의 프로퍼티들과 진짜 같을까요?
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
// 두 객체는 같을까요?
alert(obj === objCopy); // false (참조가 다름)
// 참조가 다르므로 기존 객체를 수정해도 복사본은 영향을 받지 않습니다.
obj.d = 4;
alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}
요약
"..."은 나머지 매개 변수나 스프레드 문법으로 사용가능하다.
코드 블록 { ... } 안에서 선언한 변수는 블록 안에서만 사용가능. (if, for, while 괄호에서도 동일)
함수가 살아있는 동안 이론상으로는 외부 변수 역시 메모리에 유지되지만,
실제로는 자바스크립트 엔진이 지속해서 최적화를 하기 때문에,
외부 변수가 사용되지 않는다고 판단되는 경우 메모리에서 이 값을 제거한다.
var는 블록 스코프가 없고, 변수의 중복 선언을 허용한다(두 번째 선언문이 무시됨). 또한, 선언을 먼저 하지 않고서도 사용할 수 있다.
*** 호이스팅 (Hoisting)을 통해, var로 선언한 모든 변수는 함수의 최상위로 끌어올려져 먼저 선언되기 때문.
선언만 먼저 되고 할당은 관련 코드 실행 시 처리된다.
함수가 시작되는 시점이나, 전역 공간에서는 스크립트 시작 시점에 var 변수 선언 처리가 된다.
즉시 실행 함수 표현식.
var이 블록 레벨 스코프를 가질 수 있게 만든 방안. => 현재의 모던 자바스크립트에서는 이렇게 작성할 필요 없음. (참고용)
// IIFE를 만드는 방법
(function() {
alert("함수를 괄호로 둘러싸기");
})();
(function() {
alert("전체를 괄호로 둘러싸기");
}());
!function() {
alert("표현식 앞에 비트 NOT 연산자 붙이기");
}();
+function() {
alert("표현식 앞에 단항 덧셈 연산자 붙이기");
}();
var로 선언한 전역 함수나 전역 변수는 전역 객체의 프로퍼티가 된다.
전역 객체 -> window
var gVar = 5;
alert(window.gVar); // 5 (var로 선언한 변수는 전역 객체 window의 프로퍼티가 됩니다)
만약 중요한 변수가 있고, 이를 모든 곳에서 사용할 수 있게 해야 한다면, 전역 객체에 프로퍼티 추가 가능.
가능하면 사용하지 않는 것이 좋다.
// 모든 스크립트에서 현재 사용자(current user)에 접근할 수 있게 이를 전역 객체에 추가함
window.currentUser = {
name: "John"
};
// 아래와 같은 방법으로 모든 스크립트에서 currentUser에 접근할 수 있음
alert(currentUser.name); // John
// 지역 변수 'currentUser'가 있다면
// 지역 변수와 충돌 없이 전역 객체 window에서 이를 명시적으로 가져올 수 있음
alert(window.currentUser.name); // John
가능하면 외부, 전역 변수 사용하지 않는것이 테스트가 쉽고, 에러가 적게 나는 코드를 작성하는 방법.
폴리필 사용하기
전역 객체를 이용해 현재 사용 중인 브라우저가 최신 자바스크립트 기능을 지원하는지 알 수 있음.
내장 객체 Promise 지원여부 확인하는 예)
if (!window.Promise) {
alert("구식 브라우저를 사용 중이시군요!");
}
폴리필을 직접 구현해서 사용도 가능
javascript.info - 객체 프로퍼티 설정 (0) | 2022.12.30 |
---|---|
javascript.info - 함수 심화학습(2) (0) | 2022.12.29 |
javascript.info - 자료구조와 자료형 (0) | 2022.12.28 |
javascript.info - 객체 기본(2) (0) | 2022.12.27 |
javascript.info - 객체 기본(1) (0) | 2022.12.27 |