객체
- 몇 가지 특수한 기능을 가진 연관 배열(associative array)
- 원시형(primitive type)과 달리 다양한 데이터를 담을 수 있음
- 중괄호를 이용해 만들고 프로퍼티(키-값 쌍)를 저장
** key : 문자형만 가능
** Value : 모든 자료형 가능
빈 객체를 만드는 방법
// 객체 생성자 문법
let user = new Object();
// 객체 리터럴 문법
let user = {};
객체 생성
let user = {
name:"jisu", // key : value
age: 10,
"likes birds" : true // 복수의 단어는 따옴표로 묶음
};
객체 프로퍼티 사용
- 점 표기법: 키가 유효한 식별자인 경우에만 사용 가능
** 유효한 식별자 - 공백x, 변수 이름 명명규칙 따름
- 대괄호 표기법 : 키에 어떤 문자열이 있던 상관없이 동작
// 점 표기법 (dot nation)
user.name; // 프로퍼티 값 읽기
user.isAdmin = true; // 프로퍼티 값 추가
delete user.age; // 프로퍼티 삭제
// 대괄호 표기법(square bracket notation)
user["likes birds"]; // 프로퍼티 값 읽기
user["isAdmin"] = true; // 프로퍼티 값 추가
delete user["isAdmin"]; // 프로퍼티 삭제
** 참고
const는 user의 값은 고정하지만 내용은 고정하지 않으므로 상수 객체는 수정될 수 있음
const user = {
name : "jisu"
}
user.name = "lee jisu";
console.log(user.name); // lee jisu
- 계산된 프로퍼티 : 프로퍼티 키가 대괄호로 둘러 쌓인 경우
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {
[fruit] : 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아옴
};
console.log(bag.apple); // 5
- 단축 프로퍼티 : 프로퍼티 값을 기존 변수에서 받아와 사용
function makeUser(name, age) {
return {
name : name,
age : age,
// 프로퍼티 값 단축 구문(property value shorthand)
name,
age,
isAdmin : true // 일반 프로퍼티와 섞어서 사용 가능
};
}
let user = makeUser("jisu", 10);
프로퍼티 존재 확인, 반복문
// 프로퍼티 존재 여부 확인
// undefined와 비교
console.log(user.noSuchProperty === undefined) // true -> 프로퍼티가 존재하지 않음
// in 연산자 확인
console.log("noSuchProperty" in user) // false
console.log("age" in user) // true
// for in 반복문 (객체의 모든 키 순회)
for (key in object) { // 기본 문법
...
}
// 사용 예시
let user = {
name: "jisu",
age: 10,
isAdmin: true,
}
for (let key in user) {
console.log(key); // name, age, isAdmin
console.log(user[key]); // jisu, 10, true
지금까지 위에서 설명한 객체 ⇒ " 순수 객체 (Plain Object) "
일반 객체 말고도 다양한 객체 존재함
- Array : 정렬된 데이터 컬렉션을 저장할때 사용
- Date : 날짜와 시간 정보를 저장할때 사용
- Error : 에러 정보를 저장할 때 사용
메서드와 this
- 객체 프로퍼티에 저장된 함수 ⇒ 메서드(Method)
- 메서드는 this를 사용해 객체 참조 가능
- 객체 프로퍼티에 함수를 할당해 객체에게 행동할 수 있는 능력 부여
메서드 만들기
// 일반적인 방법
let user = {
name: "jisu",
ageL 10,
};
// 메서드 생성
user.sayHi = function() {
console.log("Hello");
};
// 메서드 사용
user.sayHi(); // Hello
// 메서드 단축 구문
user = {
// 객체 리터럴 안에서 메서드 선언
sayHi: function() {
console.log("Hello");
}
};
//위 코드의 단축 구문
user = {
sayHi() { // function 생략해도 됨
console.log("Hello");
}
};
this 값은 런타임에 결정됨
- 함수를 선언할 때 this를 사용할 수 있음
- 함수가 호출되기 전까지 this에 값 할당 안됨
- 함수를 객체 프로퍼티에 저장해 object.method()같이 ‘메서드’ 형태로 호출
⇒ this는 object를 참조
let user = {
name: "jisu",
age: 10,
sayHi() {
// this는 user를 나타냄
console.log("Hello " + this.name);
}
};
user.sayHi(); // Hello jisu
화살표 함수
- 일반함수와 달리 자신만의 this를 가지지 않음
- 화살표 함수 안에서 this를 사용하면 외부에서 this 값을 가져옴
let user = {
name: "jisu",
sayHi() {
let arrow = () => console.log(this.name);
arrow();
}
};
user.sayHi(); // jisu
심볼형
- 원시형 데이터, 유일무이한 식별자를 만드는데 사용
- 유일성이 보장되는 자료형 (이름이 같더라도 값이 항상 다름)
// id는 새로운 심볼이 됨
let id = Symbol();
// 심볼에 "id"라는 설명 붙임, 디버깅시 유용
let id = Symbol("id");
전역 심볼
- 이름이 같을때 값도 같길 원할때 사용 (전역 레지스트리 사용)
** 전역 심볼 레지스트리(global symbol registry)
- 전역 심볼 레지스트리 안에 심볼 만듦 → 이름이 동일한 경우 동일한 심볼 반환
// 이름을 이용해 심볼을 찾음
// 전역 레지스트리에서 심볼을 읽습니다.
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만듭니다.
// 심볼을 이용해 이름을 얻음
let sym = Symbol.for("name");
alert( Symbol.keyFor(sym) ); // name
참조에 의한 객체 복사
- 객체는 참조에 의해(by reference) 할당되고 복사됨
- 복사된 참조를 이용한 모든 작업(프로퍼티 추가·삭제 등)은 동일한 객체를 대상으로 이뤄짐
객체와 원시 타입의 차이
// 변수에 객체에 대한 참조값이 저장됨
// 객체에 대한 참조 값 -> 객체에 저장되어 있는 메모리 주소
let user = {
name: "Jhon",
}
// 객체가 할당된 변수 복사 -> 객체 복사X, 객체의 참조값 복사
let admin = user; // 동일 객체에 대한 참조값 저장
// 원시타입 : 값 그대로 저장, 할당되고 복사됨
// message, phrase 각각에 문자열 "Hello"가 저장됨
let message = "Hello";
let phrase = message;
참조에 의한 비교
- 객체 비교 시 동등 연산자( == )와 일치 연산자( === )는 동일하게 동작
let a = {};
let b = a; // 참조에 의한 복사
console.log(a == b); // true
console.log(a === b); // true
let c = {};
let d = {};
console.log(c == d); // false
객체의 진짜 복사본을 만드는 방법
1. 얕은 복사를 가능하게 해주는 Object.assign 사용
** 중첩 객체를 처리하지 못함
2. 깊은 복사를 가능하게 해주는 _.cloneDeep(obj) 사용
new 연산자와 생성자 함수
생성자 함수
- 일반 함수와 기술적 차이 없음
- 일반 함수와 구분하기 위해 함수 이름 첫글자를 대문자로 씀
- 반드시 new 연산자와 함께 호출해야 함
** new와 함께 호출 → this가 암시적으로 만들어지고 반환됨
- 유사한 객체를 여러개 만들때 유용
function User(name) {
// this = {}; (빈 객체가 암시적으로 만들어짐)
// 새로운 프로퍼티를 this에 추가
this.name = name;
this.isAdmin = false;
this.sayHi = function() {
console.log( "제 이름은 " + this.name + "입니다." );
};
// return this; (this가 암시적으로 반환됨)
}
let user = new User("보라");
user.sayHi(); // 제 이름은 보라입니다.
alert(user.name); // 보라
alert(user.isAdmin); // false
// 인수가 없는 생성자 함수는 괄호 생략 가능
let user = new User;
객체를 원시형으로 변환하기
원시값을 기대하는 내장 함수나 연산자를 사용 → 객체-원시형으로 자동으로 형변환 됨
특수 객체 메소드 사용 → 형변환을 원하는데로 조절 가능
객체-원시형으로의 형변환 (hint 기준)
** hint : 목표로 하는 자료형 정도로 이해하면 쉬움
string | - alert()처럼 문자열을 기대하는 연산을 수행할 때 |
number | - 수학 연산을 적용하려고 할 때 |
default | - 드물게 발생 - 연산자가 기대하는 피연산자를 확실하게 알 수 없을때 발생 - 내장 객체는 hint가 default일때와 number일때 동일하게 처리함 |
// string
alert(obj);
// number
let num = Number(obj);
let n = +obj; // 단항 덧셈 연산
let greater = user1 > user2;
//default
// 이항 덧셈 연산은 hint로 `default`를 사용합니다.
let total = obj1 + obj2;
// obj == number 연산은 hint로 `default`를 사용합니다.
if (user == 1) { ... };
객체-원시형 변환 알고리즘
1. 객체에 obj[Symbol.toPrimitive](hint) 메서드가 있는지 찾고 있다면 호출
2. 1에 해당X, hint가 string일때 → obj.toString() 또는 obj.valueOf() 호출
3. 1,2에 해당X, hint가 number일때 → obj.valueOf() 또는 obj.toString() 호출
⇒ 객체에 Symbol.toPrimitive가 없으면 JS는 아래 규칙에 따라 `toString`이나 `valueOf`를 호출
- hint가 string인 경우 : toString -> valueOf 순
** toString이 있다면 toString을 호출, toString이 없다면 valueOf를 호출
- 그 외: valueOf -> toString 순
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// 데모:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
** Symbol.toPrimitive (JS 내장 심볼)
- 목표로 하는 자료형을 명명하는데 사용
- 이 메서드 하나로 모든 종류의 형변환을 다룰 수 있음
** obj.toString()만 사용해도 모든 형변환을 다룰 수 있음
출처 : https://ko.javascript.info/
'JavaScript' 카테고리의 다른 글
JavaScript 자료구조와 자료형2 (0) | 2023.03.14 |
---|---|
JavaScript 고차함수 (3) | 2023.03.13 |
JavaScript 자료구조와 자료형1 (1) | 2023.03.09 |
JavaScript 기본 문법 요약 (2) | 2023.02.28 |
JS를 이용하여 간단한 그림판 만들기 (0) | 2023.02.21 |