ko.javascript에서 Javascript를 공부를 하며 배운 내용을 개인적으로 블로그에 정리하고 학습하는 글입니다.
프로퍼티 => 서랍장 안 파일
프로퍼티 키는 문자열이나 심볼이어야 한다. (보통은 문자열)
값은 어떤 자료형이나 가능하다.
객체의 키 => 파일 각각에 붙어있는 이름표
복잡한 서랍장 안에서 이름표를 보고 원하는 파일을 쉽게 찾듯이,
객체에서 키를 이용해 프로퍼티를 쉽게 찾을 수 있다. (추가, 삭제도 마찬가지)
let user = new Object(); //객체 생성자 문법
let user = {}; // '객체 리터럴' 문법
중괄호를 이용해 객체를 선언하는 것을 객체 리터럴이라고 한다.
- 점 표기법 : obj.property
- 대괄호 표기법 : obj["property"] : 텍스트 대괄호 표기법을 사용하면 obj[varWithKey]와 같이 변수에서 키를 가져올 수 있다.
delete obj.prop : 프로퍼티를 삭제하고 싶을때
"key" in obj : 해당 key를 가진 프로퍼티가 객체 내에 있는지 확인할 때
for (let key in obj) : 프로퍼티를 나열할 때
const user = {
name : "john"
};
user.name = "Pete";
alert(user.name) // Pete
const
는 user
의 값을 고정하지만, 그 내용은 고정하지 않는다.
const
는 user
를 전체적으로 설정하려고 할 때만 오류가 발생한다.
지금까지는 '순수 객체'라 불리는 일반 객체에 대해 학습했다.
객체 이외에도 다양한 종류의 객체가 있다.
Array - 정렬된 데이터 컬렉션을 저장할 때 쓰임 (자주씀)
Date - 날짜와 시간 정보를 저장할 때 (자주씀)
Error - 에러 정보를 저장할 때
객체는 참조에 의한 객체 복사된다. 변수에는 객체
자체가 아닌 참조
가 저장이 된다.
(객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체는 복사되지 않는다.)
따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넘길 땐 객체가 아닌 객체의 참조 값
이 저장된다.
변수엔 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어있는 '메모리 주소'인 객체에 대한 '참조 값'이 저장된다.
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨
alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함
Object.assign(dest, [src1, src2, src3...])
dest
는 목표로 하는 객체
src1 ~ ...
는 복사하고자 하는 객체
src1 ~ ...
의 프로퍼티를 dest
에 복사한다.
dest
를 제외한 객체의 프로퍼티 전부가 첫 번재 객체로 복사된다.
마지막으로 dest
를 반환한다.
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // user = { name: "Pete" }
메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중 필요없게 된 영역을 해제하는 기능
가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없다.
객체는 도달 가능한 상태일 때 메모리에 남는다.
참조된다고 해서 도달 가능한 것은 아니며, 서로 연결된 객체들도 도달 불가능할 수 있다.
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("안녕하세요!");
};
user.sayHi(); // 안녕하세요!
함수 표현식으로 함수를 만들고, 객체 프로퍼티 user.sayHi
에 함수를 할당했다.
이렇게 함수를 호출하면 호출값은 안녕하세요!
가 나온다.
이렇게 객체 프로퍼티에 할당된 함수를 메서드(method) 라고 부른다.
위 예시에서는 user
에 할당된 sayHi
가 메서드이다.
객체 지향 프로그래밍(OOP)
객체를 사용하여 객체를 표현하는 방식을 객체 지향 프로그래밍(OOP)라고 부른다.
- 올바른 개체를 선택하는 방법
- 개체 사이의 상호작용을 나타내는 방법
- 위에 관한 의사결정은 객체 지향 설계를 기반으로 이뤄진다.
메서드는 객체에 저장된 정보에 접근할 수 있어야 제 역할을 할 수 있다.
대부분의 메서드가 개체 프로퍼티의 값을 활용한다.
메서드 내부에서 this
키워드를 사용하면 객체에 접근할 수 있다.
이때 '점 앞'의 this
는 객체를 나타낸다. 정확히는 메서드를 호출할때 사용된 객체.
let user = {
name: "John",
age: 30,
sayHi() {
// 'this'는 '현재 객체'를 나타냅니다.
alert(this.name);
}
};
user.sayHi(); // John
user.sayHi()
가 실행되는 동안 this
는 user
를 나타낸다.
this
를 사용하지 않고 외부 변수를 참조해 객체에 접근하는 것도 가능하다.let user = {
name: "John",
age: 30,
sayHi() {
alert(user.name); // 'this' 대신 'user'를 이용함
}
};
하지만 외부 변수를 사용해 객체를 참조하면 예상치 못한 에러가 발생할 수 있다.
user
를 복사해 다른 변수에 할당하고 (admin = user)
하고
user
는 전혀 다른 값으로 덮어썼을때, sayHi()
는 원치 않는 값(null)을 참조한다.
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // Error: Cannot read property 'name' of null
}
};
let admin = user;
user = null; // user를 null로 덮어씁니다.
admin.sayHi(); // sayHi()가 엉뚱한 객체를 참고하면서 에러가 발생했습니다.
alert
함수가 user.name
대신 this.name
을 인수로 받았으면 에러가 발생하지 않았을것.
this
를 사용할 수 있다. function sayHi() {
alert( this.name );
}
동일한 함수라도, 다른 객체에서 호출하면 this
가 참조하는 값이 달라진다.
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// 별개의 객체에서 동일한 함수를 사용함
user.f = sayHi;
admin.f = sayHi;
// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (점과 대괄호는 동일하게 동작함)
this
는 '점(.)앞의 객체를 참조하기 때문에 this
가 참조하는 값이 달라진다.
'점'과 '대괄호'는 동일하게 동작한다.
this
는 객체가 없어도 함수를 호출할 수 있다.
엄격 모드가 아닐 때(지정된 값이 없을때) this
가 전역 객체를 참조한다. 브라우저 환경에서는 window
라는 전역 객체를 참조한다.
this
를 가지지 않는다. this
를 참조한다면, 화살표 함수가 아닌 평범한 외부 함수에서 this
값을 가져오는 것이다.new
연산자와 생성자 함수
를 사용하면 유사 객체를 여러개 만들 수 있다.- 생성자 함수는 일반함수이다.
다만 일반함수와 구분을 짓기 위해 함수 이름 첫 글자를 대문자로 쓴다.
- 반드시new
연산자와 함께 호출한다.
new
와 함게 호출하면 내부에서this
가 암시적으로 만들어지고, 마지막엔this
가 반환된다
new
연산자를 붙여 실행한다.예시:
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("보라");
alert(user.name); // 보라
alert(user.isAdmin); // false
this
에 할당this
에 새로운 프로퍼티를 추가해 this
를 수정this
를 반환function User(name) {
// this = {}; (빈 객체가 암시적으로 만들어짐)
// 새로운 프로퍼티를 this에 추가함
this.name = name;
this.isAdmin = false;
// return this; (this가 암시적으로 반환됨)
}
즉 생성자의 의의는 재사용할 수 있는 객체 생성 코드를 구현하는것이다.
모든 함수는 생성자 함수가 될 수 있다는 점을 잊지 말자.
new
를 붙여 실행하면 어떤 함수라도 위에 언급된 알고리즘이 실행된다.
이름이 '첫 글자가 대문자'인 함수는new
를 실행해야 한다는것도 잊지말자.
생성자 함수에는 return문이 없다. 반환해야 할 것들은 모두 this
에 저장되고, this
는 자동으로 반환되기 때문에 반환문을 명시적으로 사용할 필요가 없다.
return
문이 있다면?return
한다면 this
대신 객체가 반환.return
한다면 return
문이 무시.return
뒤에 객체가 오면 생성자 함수는 해당 객체를 반환, 이외 경우는 this
가 반환된다.
예시:
function BigUser() {
this.name = "원숭이";
return { name: "고릴라" }; // <-- this가 아닌 새로운 객체를 반환함
}
alert( new BigUser().name ); // 고릴라
예시:_(아무것도 return하지 않았을때)
function SmallUser() {
this.name = "원숭이";
return; // <-- this를 반환함
}
alert( new SmallUser().name ); // 원숭이
?.
은 ?.
'앞'의 평가 대상이 'undefined'이나 'null'이면 평가를 멈추고 'undefined'를 반환한다.
예시:
let user = {}; // 주소 정보가 없는 사용자
alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
user?.address
로 주소를 읽으면 아래와 같이 user
객체가 존재하지 않더라도 에러가 발생하지 않는다.예시:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
위 예시를 통해 ?.
은 ?.
'앞 '평가 대상에만 동작되고, 확장은 되지 않는걸 알 수 있다.
?.앞의 변수는 꼭 선언되어 있어야 한다.
변수 user가 선언되어있지 않으면 user?.@ 평가시 에러가 발생한다.
?.
는 왼쪽 평가대상에 값이 없으면 즉시 평가를 멈춘다 이런 평가 방법을 단락 평가라고 부른다.
let user = null;
let x = 0;
user?.sayHi(x++); // 아무 일도 일어나지 않습니다.
alert(x); // 0, x는 증가하지 않습니다.
.
대신 대괄호 []
를 사용해 객체 프로퍼티에 접근하는 경우엔 ?.[]
를 사용할 수도 있다.delete user?.name; // user가 존재하면 user.name을 삭제합니다.
?.은 읽기나 삭제하기에는 사용할 수 있지만 쓰기에는 사용할 수 없다.
obj?.prop – obj가 존재하면 obj.prop을 반환하고, 그렇지 않으면 undefined
를 반환함
obj?.[prop] – obj가 존재하면 obj[prop]을 반환하고, 그렇지 않으면 undefined
를 반환함
obj?.method() – obj가 존재하면 obj.method()를 호출하고, 그렇지 않으면 undefined
를 반환함
cF. 자바스크립트는 객체 프로퍼티 키로 오직 문자형과 심볼형만을 허용한다.
숫자형, 불린형 모두 불가능하고 오직 문자형과 심볼형만 가능하다.
심볼
은 유일한 식별자를 만들고 싶을때 사용한다. Symbol()
을 사용하면 심볼값을 만들 수 있다.
// id는 새로운 심볼이 됩니다.
let id = Symbol();
심볼 이름이라 불리는 설명을 붙일 수도 있다. 심볼 이름은 디버깅 시 아주 유용하다.
// 심볼 id에는 "id"라는 설명이 붙습니다.
let id = Symbol("id");
심볼은 유일성이 보장되는 자료형이기 때문에, 설명이 동일한 심볼을 여러 개 만들어도 각 심볼값은 다르다.
심볼에 붙이는 설명(심볼 이름)은 어떤 것에도 영향을 주지 않는 이름표 역할만을 한다.
심볼은 이름이 같더라도 모두 별개로 취급된다. 하지만 이름이 같은 심볼이 같은 개체를 가리키길 원하는 경우에는 심볼 "id"
를 이용해 특정 프로퍼티에 접근해야 한다고 가정하자.
전역 심볼 레지스트리 는 이런 경우를 위해 만들어졌다. 전역 심볼 레지스트리 안에 심볼을 만들고, 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환해준다.
key
라는 이름을 가진 전역 심볼을 반환한다.
key
라는 이름을 가진 전역 심볼이 없으면 새로운 전역 심볼을 만들어준다.
key
가 같다면 Symbol.for
은 어디서 호출하든 상관없이 같은 심볼을 반환한다.
새로운 심볼을 생성하거나, 레지스트리 안에 있는 심볼을 읽으려면 Symbol.for(key)
를 사용하면 된다.
// 전역 레지스트리에서 심볼을 읽습니다.
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만듭니다.
// 동일한 이름을 이용해 심볼을 다시 읽습니다(좀 더 멀리 떨어진 코드에서도 가능합니다).
let idAgain = Symbol.for("id");
// 두 심볼은 같습니다.
alert( id === idAgain ); // true
전역 심볼 레지스트리 안에 있는 심볼은 전역 심볼이라고 불린다.
광범위하게 사용해야 하는 심볼이라면 전역 심볼을 사용하자.
Symbol.for(key)
에 반대되는 메서드. Symbol.keyFor(sym)
를 사용하면 이름을 얻을 수 있다.
// 이름을 이용해 심볼을 찾음
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
// 심볼을 이용해 이름을 얻음
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
Symbol.keyFor
는 전역 심볼 레지스트리를 뒤져서 해당 심볼의 이름을 얻어낸다.
검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용할 수 없다.
전역 심볼이 아닌 인자가 넘어오면 Symbol.keyFor는 undefined를 반환합니다.
원시값을 기대하는 내장 함수나 연산자를 사용할 때 객체-원시형으로의 형 변환이 자동으로 일어난다.
객체-원시형으로 형 변환은 hint를 기준으로 세 종류로 구분한다.
1. "string"
alert
함수같이 문자열을 기대하는 연산을 수행할 때는(객체-문자형 변환), hint가 string
이 된다.2. "number"
number
가 된다.3. "default"
default
가 된다.(드물게 발생)따라서 실무에선 hint가 "default"
인 경우와 "number"
인 경우를 합쳐서 처리하는 경우가 많다.
obj.toString()만 사용해도 '모든 변환’을 다 다룰 수 있기 때문에, 실무에선 obj.toString()만 구현해도 충분한 경우가 많다.
obj.tostring()
은 로깅이나 디버깅 목적으로 자주 사용된다.