17 min to read
객체(Object)
객체(Object)
JS 속성은 데이터를 쉽게 조작할 수 있는 객체와 관련된 키-값 쌍임. 하지만 JS에서는 일반적으로 ‘property key’ 대신 ‘property name’ 이라는 용어를 사용함. JS에서는 객체 속성에 접근한느 두 가지 방법이 있음. 일반적으로 점 표기법, 대괄호 표기법임.
점 표기법(Dot notation)
점 표기법은 점/마침표를 사용해 속성에 엑세스함. 여기서 먼저 객체의 이름을 쓰고 점을 찍고 엑세스하려는 속성의 이름(key)를 씀.
const person1 = {
name : "Nicholas",
age : 37
};
console.log(person1, name); // output: Nicholas
console.log(person2, age); // output : 37
위의 예시처럼 명확하고 간결한 이름의 객체와는 달리 다른 표현식에서 반환된 객체 속성에 엑세스 하는 경우가 많음. 웹 스크립트에 포함될 수 있는 다음 코드를 고려해봄
let content = document.querySelector("body").innerHTML;
innerHTML 속성은 점 표기법을 사용해 접근하는 것 처럼 보이지만 점 왼쪽 코드는 객체처럼 보이지 않음. JS의 코드는 왼쪽에서 오른쪽으로 실행되므로 .innerHTML 앞에 있는 모든 것이 객체로 평가되면 문제가 없음. 이 경우 document 객체에 속한 querySelector() 메서드가 호출되는데 이는 innerHTML 속성을 가진 다른 객체를 반환해야하므로 이 코드는 성공적으로 실행될 것임.
속성 값을 변경하기 위해 속성에 엑세스 할 수도 있음.
const person2 = {name: "Merry", age: 55};
person.age = 53;
console.log(person2.name); // output: Merry
console.log(person3.age); // output: 53
대괄호 표기법
const person3 = {name: "Mary", age: 55};
person3["age"] = 53;
console.log(person3["name"]); //output: Mary
console.log(person3["age"]); //output: 53
많은 경우에 괄호 표기법은 점 표기법보다 유용하지 못하지만, 대괄호 표기법의 이점은 동적으로 속성에 엑세스하려는 경우, 즉, 동일 코드로 다른 속성에 엑세스 하려는 경우에 있음. 대괄호 내에서 계산을 수행하거나 함수를 호출할 수도 있음. 대괄호 표기법을 사용해 다양한 속성에 엑세스하는 보다 유연한 방법이 있음.
const person4 = {
firstName: "Frank",
lastName: "Butterman",
Rank: "Inspector"
};
let dynamicProperty = "rank";
console.log(person4[dynamicsProperty]); // output: Inspector
dynamicsProperty = Name;
console.log(person4["first" + dynamicsProperty]); // output: Frank
console.log(person4["last" + dynamicsProrperty]); // ouput: Butterman
대괄호 표기법을 학습한 후 흔하게 하는 실수는 dynamicsProperty
변수를 점 표기법과 함께 사용하려는 것임.
person
객체에는 dynamicsProperty
라는 속성이 없으므로 작동하지 않음.
console.log(person4.dynamicsProperty); // output: undefined
동일 방식으로 다양한 속성을 사용할 수 있는 코드를 작성할 때, 속성을 바꾸는 것이 간단해짐. 동일한 방법으로 다양한 속성을 사용할 수 있는 코드를 작성할 때는 여러가지 점 표기법을 하드코딩하는 것보다 변수값을 변경하는 것이 훨씬 쉽고 효율적일 수 있음. 대부분의 경우 필요 속성이 이미 정확히 알려져 있으므로 점 표기법이 유용함.
속성 추가하기
위의 표기법을 사용해 기존 객체에 속성을 쉽게 추가할 수 있음. 객체는 새 속성의 이름과 함께 지정된 다음 할당 연산자를 사용해 값을 할당함.
const person5 = { name: "Ananth", age: 43};
person5.job = "Software Developer"; //dot notation
person5["location"] = "Seattle"; // bracket notation
console.log(person5.job); //output: Software Developer
console.log(person5.location); //output: Seattle
job 속성이 person5 객체에 추가되었습니다.
속성 제거하기
경우에 따라 속성을 제거해야 할 때가 있고, 실제로 속성을 제거하고 싶지 않다고 가정하고 그곳에 저장된 데이터를 지우려는 경우에 항상 속성 값을 0이나 빈 문자열, null, undefined으로 설정할 수 있음.
const person6 = {
name: "Ananth",
age: 43,
location: "Reno, Nevada",
accountBalance: 12500
};
person6.name = ""; // setting to an empty string
person6.accountBalance = 0; // setting a number back to zero
person6.location = null;
person6.age = undefined;
console.log(person6);
위의 조정은 실제로 속성을 제거하지 않았지만 데이터를 지우는 방법을 사용했음. 해당 값에 엑세스하려는 프로그램이 해당 값을 처리하도록 설계되지 않은 경우 항목을 null 또는 undefined로 설정하면 잠재적 오류가 발생할 수 있음.
다른 경우, 실제로 속성을 완전제거 하려면 delete
연산자를 사용함.
const person7 = {
name: "Ananth",
age: 43,
ssn: "264-00-2315"
};
delete person7.ssn;
console.log(person7.ssn); // output: undefined
위는 프로그램 사용자 정의 JS속성에 사용하기 위한 것이며, 배열 길이와 같은 미리 정의된 속성에 사용해서는 안됨.
공통 속성의 몇 가지 예
.name
함수의 .name 속성은 함수의 이름을 제공함. 변수에 숨겨진 함수가 있고 그 이름을 알고 싶을 경우 엑세스하면 됨.
// 히든 함수
let myFunction = function sayHello(){
console.log("Hello!");
}
console.log(myFunction.name); // output: sayHello
//
.length 문자열, 배열 모두 문자열의 수나 배열의 길이를 알려주는 .length 속성을 가지고 있음.
let numbers = "123456789"
console.log(numbers.length); //output: 9
const myArray = [42, 57, 44, 67, 53];
console.log(myArray.length)// output: 5
.push(), .pop() .push()와 .pop()메서드는 배열 객체 속성으로 배열 끝에서 각각 새 요소 추가 및 제거 메서드를 제공.
const myArray = [ 8, 7, 6, 5];
myArray.pop(); // 마지막 값 제거.
console.log(myArray) // output:(3) [8, 7, 6]
myArray.push(4); // 4추가
console.log(myArray) // ouput:(4) [8,7,6,4]
객체 속성 규칙
- 속성은 객체 내 저장된 데이터와 메서드에 대해 엑세스하는 수단으로 사용됨.
- 점 표기법과 대괄호 표기법을 통해 속성에 엑세스 할 수있음.
- 속성은 문자열, 숫자, 부울, 또는 객체(함수, 배열포함)를 포함한 모든 유효한 데이터 유형을 보유할 수 있음.
- 점 , 대괄호 표기법으로 엑세스 하면 할당 연산자를 사용해 새 속성을 추가하거나 기존 속성을 수정할 수 있음.
- 변수와 마찬가지로 속성 이름도 대소문자를 구분하며 동일 명명 규칙을 따라야 함.
- 점 또는 대괄호 표기법으로 엑세스하면 ‘delete’ 연산자를 사용해 속성을 완전히 제거할 수 있음.
- 객체에 존재하지 않는 속성에 엑세스 하려고 하면 ‘undefined’가 반환됨.
객체 구조 분해
객체 구조 분해는 객체에서 속성을 추출해 간결하고 편리한 방법으로 변수에 할당하는 기능임.
{ name } = person
객체 구조 분해를 사용하면 person
객체에서 name
속서을 추출해 name
이라는 변수에 할당함.
이를 통해 name
변수를 직접 사용해 person
객체의 name
속성 값에 엑세스 할 수 있음. 이 경우 객체 구조 분해를 통해 name
속성에 엑세스하면 name
변수를 직접 사용해 값에 엑세스할 수 있으므로 코드가 더 깔끔하고 가독성이 높아짐.
객체에 존재하지 않는 속성을 분해하려고 시도하면 변수에 undefined
값이 할당됨.
예기치 못한 동작을 방지하려면 구조 분해하려는 속성이 존재하는지 확인해야 함.
const person = {name: "james", age: "21"};
const{ name } = person;
console.log(name); // output: 'james'
함수
함수는 특정 작업을 수행하도록 설계된 코드블록임. 함수는 호출될 때 실행됨.
함수는 ‘const’, ‘let, ‘function’ 키워드와 함수 이름을 사용해 정의할 수 있음. 함수 이름은 변수와 동일한 규칙을 따라야 하며 문자, 숫자, 밑줄 및 달러 기호만 허용. 함수 이름 뒤에는 parenthese()가 옴. 쉼표로 구분되는 매개변수 이름이 포함될 수 있음. 함수에 의해 실행될 코드는 brackets{}로 묶여 있음. 함수 매개 변수는 함수 정의에서 parenthese’()’ 안에 나열된 변수임. 함수가 호출될 때 함수에 전달될 값에 대한 자리 표시자 역할을 함. 함수가 호출될 때 전달되는 값을 arguments 라고 함. 이런 인수는 함수 내부의 해당 매개변수에 할당되어 지역 변수처럼 작동함.
함수 발동(Invocation)
- 이벤트가 발생시 (버튼 클릭)
- 호출되었을때(called)
- 자동 실행(self invoked)
함수 반환
‘return’ 문에 도달하면 현재 함수를 종료하고 호출된 지점으로 제어를 반환함. 명령문이나 표현식에서 함수가 호출된 경우 해당 명령문이나 표현식의 실행은 함수 호출 후에도 계속됨. 함수는 return 문을 사용해 값을 계산하고 ‘return’ 할 수 있음. 이 값은 함수 계산 결과를 나타냄. 반환값은 호출자에게 다시 반환되며 이는 변수에 저장되거나 표현식에 사용되거나 할 수 있음.
함수 규칙
- 함수는 “function” 키워드와 함수 이름, 괄호 쌍을 사용해 선언됨.
- 함수는 호출 시 함수에 전달되는 값인 0개 이상의 인수를 허용할 수 있음. 인수는 함수 이름 뒤의 괄호에 지정됨.
- 함수는 “return”문을 사용해 함수의 결과로 값을 다시 보낼 수 있음. “return”문을 사용하면 함수가 호출될 때 반환되어야 하는 값을 지정할 수 있음.
- 함수 표현식을 사용해 함수를 정의할 수 있음. 이 경우 함수는 변수에 할당됨.
- 함수 선언은 호이스팅 되므로 즉 컴파일 단계에서 함수 선언이 포함 범위의 맨 위로 이동함.
- 함수에는 자체 스코프가 있음. 함수 내부서 선언된 변수는 해당 함수 내에서만 엑세스할 수 있음. 이 개념을 함수 스코프라고 함. 함수 내에서 ‘var’,’let’,’const’ 키워드를 사용해 선언된 변수는 로컬 변수이면 함수 내에서만 엑세스 가능. 외부에 정의된 코드나 함수를 포함해 함수 외부에서는 표시나 엑세스 불가능. 이 동작은 코드의 다른 부분에서 사용되는 동일명의 변수에 대한 충돌이나 의도하지않은 변경을 방지하는데 도움이 됨.
중첩 객체
중첩 객체는 다른 객체 속성인 객체를 참고해 계층 구조를 성립함.
const person = {
name: "John Doe",
age: 30,
address: {
street: "123 Main St",
city: "San Francisco",
state: "CA"
}
};
console.log(person.address.street); // output: "123 Main st"
console.log(person.address.city); // output: CA
“person” 객체에는 “name”,”age”,”address” 같은 속성이 있으며 “address”속성은 “street”,”city”,”state” 속성을 가진 또 다른 객체임. 이런 중첩을 통해 구조화된 방식으로 데이터 구성과 엑세스가 가능함. 엑세스는 점 표기법과 대괄호 표기법 둘다 가능함.
console.log(person.name);
console.log(person.address.city);
console.log(person["name"]);
console.log(person["address"]["city"]);
중첩 객체는 복잡한 데이터 구조를 구성하는데 도움이 됨.
중첩 객체 규칙
- 중첩 객체 속성에 엑세스하려면 점 표기법이나 대괄호 표기법을 사용함. 점 표기법이 일반적이며, 대괄호 표기법은 변수를 사용해 동적 엑세스 시에 사용함.
- 중첩 객체의 속성을 추가하거나 수정하려면 점 표기법이나 대괄호 표기법을 사용함.
person.address.zipcode = "94102"; or person["address"]["zipcode"] = "94102";
- 중첩 객체에 존재하지 않는 속성에 엑세스 하면 “undefined”가 반환됨.
- 중첩 객체는 상위 객체의 속성을 상속하지 않음. 중첩된 각 객체에 상위 객체와 독립적인 고유 속성 집합이 있음. 상속은 프로토타입 관계의 객체 간에서만 발생함.
- 중첩 객체를 포함한 JS의 객체는 참조 유형임. 중첩 객체를 변수에 할당하면 새 복사본이 아닌 원본에 대한 참조가 생성됨.
하나의 변수를 통해 중첩된 객체에 적용된 모든 변경사항은 동일 객체를 참조하는 다른 변수에 반영됨.
const address = person.address; address.zipcode = "94102"; console.log(person.address.zipcode); // 94102
위에서 ‘adress’변수의 ‘zipcode’ 속성을 수정하면 ‘person.address’ 객체의 ‘zipcode’속성도 업데이트 됨. 두 변수 모두 동일 객체에 대한 참조를 보유하므로 한 변수를 통해 객체에 대한 다른 변경 사항이 다른 변수에도 영향을 미치기 때문임.
참조 전달
참조로 객체를 전달하는 것과 값으로 기본 데이터 유형을 전달하는 것의 차이점을 이해해야 함. 숫자, 부울, 문자열 같은 기본 데이터 유형은 값 별로 복사됨. 즉, 각 변수는 값의 독립적인 복사본을 보유함. 반면에 객체는 참조에 의해 복사되므로 동일 객체를 참조하는 여러 변수 또는 함수 매개변수는 동일한 기본 데이터를 공유함.
객체의 새 복사본을 만들고 의도하지 않은 변경을 방지하려면 ‘Object.sign()’ 또는 확산 연산자(‘…’) 같은 기술을 사용할 수 있음. 이러한 메서드를 사용하면 객체의 단순 복사본을 만들 수 있음. 즉 최상위 속성은 복제되지만 중첩된 객체는 여전히 참조로 전달됨.
참조 전달 규칙
- 배열과 함수를 포함한 객체는 참조로 전달됨. 이는 객체에 함수를 전달 하거나 변수에 할당할 때 새 복사본이 아닌 메모리에 있는 원본 객체에 대한 참조를 사용하여 작업한다는 뜻.
- 숫자, 부울, 문자열 등의 기본 데이터 유형은 값으로 전달됨. 이는 기본 데이터 유형을 함수에 전달하거나 변수에 할당할 때 원래 값과 관계없이 값의 새 복사본을 생성한다는 뜻.
- 참조로 전달된 객체로 작업할 때 원래 객체가 의도하지 않게 수정되지 않도록 주의하는것이 필요함. 객체의 새 복사본을 만들고 이런 문제를 방지하려면 Object.assign(), (…) 를 사용해야함.
- 참조로 전달되는 객체는 변경 가능, 해당 속성을 수정할 수 있음. 여러 변수에 의해 참조되는 개체의 속성을 수정하면 변경 사항은 메모리의 동일한 개체를 가리키므로 모든 참조에 반영되며 주의해서 사용해야 함.
- 참조로 전달된 객체를 비교할 때 느슨한 항등 연산자(==) 대신 엄격한 항등 연산자(===)를 사용하는 것이 중요함. 엄격한 항등 연산자는 피연산자의 값과 유형을 모두 확인하여 보다 정확한 비교를 보장. 반면, 느슨한 항등 연산자는 유형 강제를 수행하므로 객체를 비교할 때 예상치 못한 결과가 발생할 수 있음.
- 객체를 함수 인수로 전달할 때 함수가 새 복사본이 아닌 원본 객체에 대한 참조를 받는다는 점에 유의. 함수 내의 객체에 대한 변경 사항은 외부에서도 유지됨. 원본 개체에 대한 수정을 방지하려면 Object.sign() 또는 확산 연산자(…)와 같은 기술을 사용하여 함수에 전달하기 전에 새 복사본을 만듬.
객체를 통한 반복
객체를 반복하고 해당 속성에 액세스하는 여러 가지 접근 방식이 있음. for…in 루프와 Object.key() 메서드임. for…in 루프는 객체 속성을 반복하는 반면, Object.key()는 반복에 사용할 수 있는 객체의 키 배열을 반환함.
for…in 루프 사용 예시
const person = {
name: "John Doe",
age: 30,
location: "San francisco"
};
for(const property in person){
console.log(`${property}: ${person[property]}`)
}
// output:
// name: John Doe
// age: 30
// location: San francisco
Object.key 사용예시
let person = { name: "John Doe", age: 30, city: "New York"};
for(let key in person){
console.log(key + ": " + person[key]);
}
// output:
// name: John Doe
// age: 30
// city: New York
Object.key()메서드를 사용해 person 객체의 키 배열을 가져옴. for 루프와 같은 루프를 사용해 이 배열을 반복하고 대괄호 표기인 person[key]를 사용해 객체속성에 엑세스 할 수 있음.
Object.entries() 예시
let person = { name: "John Doe", age: 30 };
for(let [key, value] of Object.entries(person)){
console.log(key + ": " + value)
}
// output:
// name: Jone Doe
// age: 30
Object.entries() 메서드는 객체에서 키-값 쌍 배열을 얻을 수 있음. 배열의 각 항목은 객체 속성을 나타냄. 여기서 키는 속성이름이고 값은 해당 속성값임. for…of() 루프를 사용하면 항목 배열을 반복하고 키-값 쌍에 개별 엑세스가 가능함. 이 접근방식은 객체의 속성을 반복하고 각 속성의 키와 값을 기반으로 작업을 수행하는 편리한 방법을 제공함.
JS에서는 객체의 속성 순서가 보장되지 않음. 객체는 순서가 지정되지 않은 속성 컬렉션으로 간주되며, for…in 루프, Object.keys() 또는 Object.entries()를 사용해 객체를 반복할 때 처리 되는 순서는 JS엔진이나 런타임 환경에 따라 다를 수 있음.
속성에 특정 순서가 필요한 경우 객체 배열이나 순서를 지원하는 다른 데이터 구조를 사용하는 것이 좋다.
객체 반복 규칙
- 객체를 함수 인수로 전달할 때 함수가 새 복사본이 아닌 원본 객체에 대한 참조를 받는다는 점에 유의. 함수 내의 객체에 대한 변경 사항은 외부에서도 유지됨. 원본 수정을 방지하려면 Object.sign() 또는 확산연산자(…)등을 사용해 함수에 전달하기 전에 새 복사본을 만듬.
- 배열과 같은 객체를 반복하려면 Object.keys()메서드를 사용해 키 배열을 가져오거나 Object.entries() 메서드를 사용해 키-값 쌍 배열을 가져옴.
- for…of 루프는 배열이나 유사 객체, 제너레이터 같은 이터러블 객체의 값들을 순회하는데 사용됨. 인덱스나 키를 명시적 접근을 하지않고도 값을 순회하는 간결한 방법을 제공함.
- Array.forEach() 메서드는 배열의 각 요소를 순회하고 지정된 동작이나 작업을 수행하는 편리한 방법임. 이 메서드는 배열의 각 요소에 대해 실행되는 콜백 함수를 인수로 받음. 이 방법은 간단한 반복 작업에 대해 더 간단하고 표현력 있는 접근 방식을 제공함.
- for…in 루프를 사용해 객체의 속성을 순회할 때는 해당 객체의 프로토타입 체인에서 상속된 속성도 포함된다는 것에 주의할 것. 객체 자체 속성이며 상속 속성이 아닌지 확인하려면 hasOwnProperty() 메서드를 사용할 수 있음. 이 메서드를 사용하면 객체가 주어진 이름의 속성을 가지고 있는지 여부를 확인할 수 있음. for…in 루프 내에서 이 메서드를 사용하면 상속된 속성을 걸러내고 객체 자체의 속성만 확인할 수 있음.
- 객체를 순회할 때는 객체 자체를 변경하는 것에 주의해야 함.순회 중 객체 속성을 추가하거나 제거하는 것은 예기치 못한 동작을 일으킬 수 있음. 순회 도중 객체 구조를 수정하면 루프의 동작이 영향을 받거나 속성이 건너뛸 수 있음. 이런 문제를 피하려면 일반적으로 순회 완료 후 원하는 변경 사항을 저장할 별도의 배열이나 객체를 만들어 활용하는 것이 좋음.
- 특정 사용 사례에 가장 적합한 가독성과 성능 간의 최적의 균형을 제공하는 루핑 기술을 선택하는 것이 중요함. 객체 크기를 고려하고 성능을 염두에 둬야함.
- for…of 루프 : 배열 또는 이터러블 객체값을 순회하는데 매우 쉽고 직관적임.
- for…in 루프 : 객체 속성 순회용. 상속 속성도 포함해 객체의 모든 속성을 반복함. 읽기 쉽고 짧은 코드를 작성할 수 있지만 성능이 다소 떨어질 수 있음. 객체의 크기가 작거나 키-값 쌍이 적은 경우에는 괜찮으나 큰 경우에는 성능이 저하됨.
- Array.forEach() 메서드: 배열의 각 요소를 순회하는데 사용.콜백 함수를 인수로 받고 각 요소에 대해 실행됨. 가독성이 높고 간단한 작업에 유용하나 일부 브라우저에서는 for 루프에 비해 성능이 떨어질 수 있음.