객체
참조를 통한 복사
let fruits = ["딸기"]
let arr = fruits; // ✅ 참조를 복사함(두 변수가 같은 객체를 참조)
alert( arr === fruits ); // true
arr.push("망고"); // 참조를 이용해 배열 수정
alert( fruits ); // 딸기,망고
// ✅ 요소에 여러 가지 자료형이 섞여 있을 수 있다!
let arr = [ '사과', { name: '이보라' }, true, function() { alert('안녕하세요.'); } ];
//✅ unshift : 배열 앞에 요소를 추가한다.
let fruits = ["오렌지", "배"];
fruits.unshift('사과');
alert( fruits ); // 사과,오렌지,배
//✅ shift : 배열 앞에 요소를 제거하고 제거한 요소를 반환한다.
const removed = fruits.shift();
alert(removed); // 사과
//✅ push, unshift는 여러개도 한 번에 가능
let fruits = ["사과"];
fruits.push("레몬", "귤");
fruits.unshift("파인애플", "레몬");
// ["파인애플", "레몬", "사과", "레몬", "귤"]
alert( fruits );
💡 이유 - shift, unshift 연산의 동작 원리
1. 인덱스가 0인 요소를 제거, 추가
2. 모든 요소를 왼쪽으로 이동. 이때 인덱스 1은 0, 2는 1로
3. length 프로퍼티 값 갱신
for ~ of
: 값만 얻을 수 있다.let fruits = ["귤", "망고", "딸기"];
for (let fruit of fruits) {
alert( fruit ); // 귤, 망고, 딸기
}
for ~ in
: 인덱스를 얻는다. (배열에는 되도록 추천 X)💡 for~in 사용시 주의 사항
1. for..in 반복문은 모든 프로퍼티를 대상으로 순회히여 키가 숫자가 아닌 프로퍼티도 순회 대상에 포함된다.
2. for..in 반복문은 배열이 아니라 객체와 함께 사용할 때 최적화되어 있어서 배열에 사용하면 객체에 사용하는 것 대비 10~100배 정도 느리다.
arr.length = 0
을 사용해 간단하게 배열을 비우기 가능let fruits = ["귤", "망고", "딸기"];
fruits.length = 2; // ✅ 요소 2개만 남기고 자름
alert( fruits ); // [귤, 망고]
fruits.length = 5; // 본래 길이로 되돌려 봅시다.
alert( fruits[3] ); // undefined
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
alert( matrix[1][1] ); // 5, 중심에 있는 요소
let arr = [1, 2, 3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true
💡 delete를 사용해 요소를 지운다면?
배열의 길이가 -1 되기를 기대했지만, 그렇지 않다. 따라서 splice를 사용하는 것!delete arr[1]; // "go"를 삭제합니다. alert( arr[1] ); // ✅ undefined alert( arr.length ); // ✅ 여전히 길이가 3
let arr = ["I", "study", "JavaScript"];
const newArr = arr.splice(1, 2); // ✅ 인덱스 1부터 요소 2개를 제거
alert(newArr); //["JavaScript"]
let arr = ["I", "study", "JavaScript", "right", "now"];
// ✅ 처음 3개의 요소를 지우고 ("I", "study", "JavaScript"), 이 자리를 "Let's", "dance"로 대체
arr.splice(0, 3, "Let's", "dance");
alert( arr ) // ["Let's", "dance", "right", "now"]
💡 음수 인덱스도 사용할 수 있다!
마이너스 부호 앞의 숫자는 배열 끝에서부터 센 요소 위치를 나타낸다.let arr = [1, 2, 5];
// ✅ 인덱스 -1부터 (배열 끝에서부터 첫 번째 요소) 1개의 요소를 삭제하고 3과 4를 추가합니다.
arr.splice(-1, 1, 3, 4);
alert( arr ); // 1,2,3,4
### slice : 요소 복사 시 사용
- 인자가 두 개 들어가는 경우 마지막 인자로 들어간 인덱스는 제외된다는 것을 명심하자
```js
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s
alert( arr.slice(-2) ); // s,t (인덱스가 -2인 요소부터 제일 끝 요소까지를 복사)
let arr = [1, 2];
alert( arr.concat([3, 4]) ); // 1,2,3,4
// arr의 요소 모두와 [3,4]의 요소 모두, [5,6]의 요소 모두를 모은 새로운 배열이 만들어집니다.
alert( arr.concat([3, 4], [5, 6]) ); // ✅ 1,2,3,4,5,6
arr.indexOf(item, from)
: from부터 시작해 item을 찾는다. 요소를 발견하면 해당 요소의 인덱스
를 반환하고, 발견하지 못했으면 -1
을 반환한다. arr.lastIndexOf(item, from)
: indexOf와 같은 기능을 하지만, 배열 끝에서부터 검색한다. arr.includes(item, from)
: indexOf와 같은 기능을 하지만, 해당 요소를 발견하면 true
를 반환한다. let arr = [1, 0, false];
alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.lastIndexOf(false) ); // 2
alert( arr.includes(1) ); // true
find
, findIndex
를 이용할 수 있다.find
: 특정조건에 부합하는 요소 자체를 리턴findIndex
: 조건에 부합하는 요소의 인덱스를 리턴한다. let users = [
{id: 1, name: "Heather"},
{id: 2, name: "Matthew"},
{id: 3, name: "Ricky"}
];
let user = users.find(item => item.id == 1);
let userIndex = users.findIndex(item => item.name == "Heather");
alert(user.name); // Heather
alert(userIndex); //0
filter
를 사용한다.filter
: 조건에 맞는 요소 전체를 담은 배열을 반환 let user = users.filter(item => item.id > 1);
alert(user.length); // 2
alert(user); //[object Object], [object Object]
map
: 배열 요소 전체를 대상으로 함수를 호출하고, 함수 호출 결과를 배열로 반환 // ✅ 각 요소마다의 length를 리턴한 배열을 반환
let lengths = ["Heather", "Matthew", "Ricky"].map(item => item.length);
alert(lengths); // 7,7,5
sort
: 배열 요소 전체를 정렬한다. (quick sort 이용하여 정렬한다고 한다!)💡
sort
에서 요소는 문자열로 취급되어 재정렬된다.let arr = [ 1, 2, 15 ]; arr.sort(); // ✅ 문자열 비교는 사전편집 순으로 진행되어 15가 2보다 큰 값으로 취급된다. alert( arr ); // 1, 15, 2
따라서 기본 정렬 기준 대신 새로운 정렬 기준을 만드려면 arr.sort()에 새로운 함수를 넘겨줘야한다!
function compareNumeric(a, b) { if (a > b) return 1; if (a == b) return 0; if (a < b) return -1; } let arr = [ 1, 2, 15 ]; arr.sort(compareNumeric); alert(arr); // 1, 2, 15 ✅ 기대했던 대로 요소가 정렬됨
보통은 화살표 함수를 사용해서 깔끔하게 나타낸다!
let arr = [ 1, 2, 15 ];
arr.sort( (a, b) => a - b );
alert(arr); // 1, 2, 15
[sort의 내부 동작은 해당 링크를 참고하면 더 쉽게 이해가 된다!](https://noirstar.tistory.com/359)
> 💡 문자열을 언어 상관없이 사전 순으로 정렬하고 싶다면? ** localCompare ** 를 사용하자!
`str.localeCompare` 메서드는 유니코드를 기준으로 글자를 비교하기 때문에 독일어와 같은 언어도 사전 순서대로 정렬해준다!
```js
let countries = ['Österreich', 'Andorra', 'Vietnam'];
alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (제대로 정렬 X)
alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (제대로 정렬)
split
: 문자열을 배열로 쪼개준다. (메일 발신자를 ,로 구분해서 여러명 작성했을 경우 유용!)join
: 배열요소를 문자열로 합쳐준다let names = 'Heather, Mary, Eunice';
let arr = names.split(', '); // ✅ 인자 안에 들어간 구분자를 기준으로 나눈다
for (let name of arr) {
alert( `${name}에게 보내는 메시지` ); // Heather에게 보내는 메시지
}
let nameStr = arr.join(',');// ✅ 인자 안에 들어간 구분자를 사용해 합친다.
alert(nameStr); //Heather,Mary,Eunice
reduce
, reduceRight
모두 배열의 각 요소에 대해 주어진 리듀서 (reducer) 함수를 실행하고, 하나의 결과값을 반환하는데, reduceRight
는 연산을 배열 끝에서부터 사용한다는 점이 다르다. let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current,0 ) => sum + current);
alert( result ); // 15
💡
reduce
,reduceRight
에서 초깃값 생략이 가능하다.
초깃값이 없으면 배열 첫번쨰 요소를 초깃값으로 사용한다.
초깃값이 없이 사용할 때 만약 배열이 비어있는 상태면 reduce 호출 시 에러가 발생하기 때문에 주의해야한다.
따라서 초깃값을 항상 지정해주는 것을 추천한다!
typeof []
의 결과는 object이다. isArray
를 사용해서 배열인지 아닌지를 판단할 수 있다. alert(Array.isArray({})); // false
alert(Array.isArray([])); // true
thisArg
thisArg
라는 매개변수를 옵션으로 받을 수 있다. thisArg
는 func의 thislet army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age < this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30}
];
// ✅ 두번째 인자로 들어간 army : canJoin의 this
// ✅ 두번째 인자를 넣어주지 않으면 army.canJoin가 단독함수로 처리돼서 this를 찾을 수 없어서 this가 undefined가 되고, 에러를 뱉을 것!
let soldiers = users.filter(army.canJoin, army);
💡 배열이 아닌 객체가 있는데, 이 객체가 어떤 것들의 컬렉션(목록, 집합 등)을 나타내고 있는 경우, for..of 문법을 적용할 수만 있다면 컬렉션을 순회하는데 유용하지 않을까?
let range = {
from: 1,
to: 5
};
// 1. ✅ for..of 최초 호출 시, Symbol.iterator가 호출됨
range[Symbol.iterator] = function() {
// ✅ Symbol.iterator는 이터레이터 객체를 반환
// 2. 이후 for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해진다.
return {
current: this.from,
last: this.to,
// 3. ✅ for..of 반복문에 의해 반복마다 next()가 호출
next() {
// 4. ✅ next()는 값을 객체 {done:.., value :...}형태로 반환
// ✅ done : true - 반복이 종료되었음
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
for (let num of range) {
alert(num); // ✅ 1, then 2, 3, 4, 5
}
range에는 next()
가 없지만, range[Symbol.iterator]()
를 호출해서 만든 ‘이터레이터’ 객체와 이 객체의 메서드 next()
에서 반복에 사용될 값을 만들어낸다.
더 간단하게 만들 수도 있다.
let range = {
from: 1,
to: 5,
// ✅ 이제 range[Symbol.iterator]가 객체 range 자체를 반환
[Symbol.iterator]() {
this.current = this.from;
return this;
},
// ✅ 반환된 객체엔 필수 메서드인 next()
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
for (let char of "test") {
// ✅ 글자 하나당 한 번 실행
alert( char ); // t, e, s, t가 차례대로 출력됨
}
for..of
없이도 for..of
를 사용한 것과 동일한 작업을 한다. let str = "Hello";
// ✅ for..of를 사용한 것과 동일한 작업을 합니다.
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next(); // ✅ 반복 과정을 더 명시적으로 통제할 수 있다.
if (result.done) break;
alert(result.value);
}
이터러블(iterable)
: 메서드 Symbol.iterator가 구현된 객체유사 배열(array-like)
: 인덱스와 length 프로퍼티가 있어서 배열처럼 보이는 객체let arrayLike = { // ✅ 인덱스와 length프로퍼티가 있음 => 유사 배열
0: "Hello",
1: "World",
length: 2
};
둘 다 대개 배열이 아니기 때문에 배열에 사용할 수 있는 메서드를 지원하지 않는다.
💡 이터러블과 유사배열을 받아 진짜 배열을 만드는 Array.from() 을 사용하자!
let arrayLike = { 0: "Hello", 1: "World", length: 2 };
let arr = Array.from(arrayLike);
alert(arr.pop()); // ✅ World (메서드가 제대로 동작)
#### Array.from의 매핑 함수
- 새로운 배열에 요소를 추가하기 전에 각 요소를 대상으로 mapFn을 적용할 수 있다. 새로운 배열엔 mapFn을 적용하고 반환된 값이 추가된다.
```js
// 각 숫자를 제곱
let arr = Array.from(range, num => num * num);
alert(arr); // 1,4,9,16,25