본문 바로가기

자바스크립트

객체: 기초

객체

객체는 중괄호{...}를 이용해 생성.

키(key):값(value) 쌍으로 구성된 프로퍼티(property)를 여러 개 넣을 수 있음.

키 에는 문자형, 값에는 모든 자료형이 허용.

let user = new Object(); // 객체 생성자 문법
let user = {}; // 객체 리터럴 문법

프로퍼티 삭제

let user = {
	name: "kim",
	age: 9,
	"likes birds": true,
};

user.isAdmin = true;
delete user.age;

user.likes birds // error
user["likes birds"];

점 표기법, 대괄호 표기법

let user = {
  name: 'kim',
  age:1,
};

let key = prompt('사용자 어떤 정보 원해?', "name");
alert(user[key]);

let key2 = "name";
alert(user.key2); // undifined

프로퍼티 나열

let user = {
  name: 'kim',
  age:1,
};

console.log("age" in user); // true
console.log("blabla" in user); // false
let user = {
  name: 'kim',
  age: 1,
  isAdmin: true,
};

for (let key in user) {
  console.log(key);
  console.log(user[key]);
}

참조에 의한 객체 복사

객체와 원시 타입의 근본적인 차이 중 하나는 객체는 '참조에 의해(by ference)' 저장되고 복사 된다는 것.

참조에 의한 비교

let a = {};
let b = a;
console.log(a == b);
console.log(a === b);

let a1 = {};
let b2 = {};
console.log(a1 == b2);

객체 복사, 병합과 Object.assign

let user = {
    name: 'kim',
    age: 1
};

let clone = {};
for (let key in user) {
    clone[key] = user[key];
}

let permission1 = {canView: true};
let permission2 = {canEdit: true};

Object.assign(user, permission1, permission2);

let clone2 = Object.assign({}, user);

중첩객체복사

let user = {
    name: 'kim',
    sizes: {
        height: 222,
        width: 22
    }
};

let clone = Object.assign({}, user);

console.log(user.sizes === clone.sizes);

객체의 '진짜 복사본'을 만들려면 '얕은 복사(shallow copy)'를 가능하게 해주는 Object.assign 이나 '깊은 복사'를 가능하게 해주는 _.cloneDeep(obj)를 사용하면 됩니다. 얕은 복사본은 중첩 객체를 처리하지 못하는 점.


가비지 컬렉션

자바스크립트는 도달 가능성(reachability) 이라는 개념을 사용해 메모리 관리를 수행

'도달 가능한(reachable)' 값은 쉽게 말해 어떻게든 접근하거나 사용할 수 있는 값을 의미. 도달 가능한 값은 메모리에서 삭제되지 않음.

내부 알고리즘('mark-and-sweep')

  1. 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 'mark(기억)' 합니다.
  2. 루트가 참조하고 있는 모든 객체를 방문하고 이것을 'mark' 합니다.
  3. mark된 모든 객체에 방문하고 그 객체들이 참조하는 객체로 mark 합니다. 한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하는 일은 없습니다.
  4. 루트에 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복합니다.
  5. mark 되지 않은 모든 객체를 메모리에서 삭제합니다.

메서드와 'this'

자바스크립트에서 this는 런타임에서 결정됩니다. 메서드가 어디서 정의되었는지 상관없이 this는 '점 앞의 객체가 무엇인가에 따라' 자유롭게 결정됩니다.

객체 리터럴 안에 메서드를 선언할 때 function 생략해도 메서드 정의 가능.

let user = {
    sayHi: function() {
        alert('hi');
    }
};

let user2 = {
    sayHi() {
        alert('hi');
    }
};

자바스크립트의 this는 런타임에서 결정.

let user = {name: 'user'};
let admin = {name: 'admin'};

function sayhi () {
    alert(this.name);
}

user.f = sayhi; // user
admin.f= sayhi; // admin

엄격 모드가 아닐 때는 [object window], 엄격 모드일 경우 undefined

function sayHi () {
    alert(this);
}

별개의 this가 만들어지는 건 원하지 않고, 외부 컨텍스트에 있는 this를 이용하고 싶은 경우 화살표 함수가 유용합니다. ................ ???? //todo

let user = {
    firstName: "보라",
    sayHi() {
        let arrow = () => alert(this.firstName);
        arrow();
    }
};

문제 // todo

function makeUser() {
  return {
    name: "John",
    ref: this
  };
};

let user = makeUser();

alert( user.ref.name );

'new' 연산자와 생성자 함수

생성자 함수

  1. 함수 이름의 첫 글자는 대문자로 시작
  2. 반드시 "new" 연산자를 붙여서 실행
function User(name) {
    this.name = name;
    this.isAdmin = false;
}

let user = new User("kim");
console.log(user);

생성자 함수에는 보통 return 없음, 반환해야 할 것들은 모두 this에 저장되고, this는 자동 반환


옵셔널 체이닝 '?.'

옵셔널 체이닝(optional chaining) ?. 을 사용하면 프로퍼티가 없는 중첩 객체에도 에러 없이 안전하게 접근할 수 있습니다.

  1. 객체의 '숨김' 프로퍼티
  2. 자바스크립트 내부에서 사용되는 시스템 심볼은 Symbol.* 로 접근 가능.

?. 앞의 평가 대상이 undefined나 null이면 평가를 멈추고 undefined를 반환합니다.

let user = {};
alert(user.address.street); // error :Uncaught TypeError: Cannot read property 'strreet' of undefined

alert(user?.address?.strret); // undefined
  1. obj?.prob - obj가 존재하면 obj.prop을 반환하고, 그렇지 않으면 undefined를 반환.
  2. obj?.[prob] - obj가 존재하면 obj[prop]을 반환하고, 그렇지 않으면 undefined를 반환.
  3. obj?.method() - obj가 존재하면 obj.method()를 호출하고, 그렇지 않으면 undefined를 반환.

심볼형

심볼(Symbol)은 원시형 데이터로 유일한 식별자(unique identifier)를 만들고 싶을 때 사용합니다.

심볼형 값은 다른 자료형으로 암시적 형 변환(자동 형 변환)이 되지 않습니다.

let id = Symbol();
let id2 = Symbol("id");

let id3 = Symbol("id");
let id4 = Symbol("id");

console.log(id3 == id4); // false 심볼은 유일성이 보장되는 자료형이기 때문에, 설명이 동일한 심볼을 여러 개 만들어도 각 심볼값은 다름.

for..in 반복문에서 배제, 직접 접근만 가능.

let id = Symbol("id");
let user = {
    user: 'kim',
    [id]: 123
};

전역 심볼, 등록, 이름, 찾기

let id = Symbol.for("id"); // 전역 레지스트리에서 심볼 읽음. 존재하지 않으면 새로 만듦.

let idAgain = Symbol.for("id");

console.log(id === idAgain); // true

let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

console.log(Symbol.keyFor(sym));
console.log(Symbol.keyFor(sym2));

객체를 원시형으로 변환하기

  1. 객체는 논리 평가시 true를 반환합니다. 따라서 객체는 숫자형이나 문자형으로만 형 변환이 일어난다고 생각하시면 됩니다.
  2. 숫자형으로의 형 변환은 객체끼리 빼는 연산을 할 때나 수학 관련 함수를 적용할 때 일어남.
  3. 문자형으로의 형 변환은 대게 alert(obj) 같이 객체를 출력하려고 할 때 일어남.

객체 형 변환은 세 종류로 구분됨. (string, number, default-연산자가 기대하는 자료형이 확실치 않을 때)

'hint' : 구분 기준, 목표로 하는 자료형

자바스크립트는 형 변환이 필요할 때, 아래와 같은 알고리즘에 따라 원하는 메서드를 찾고 호출함

  1. 객체에 obj[Symbol.toPrimitive](hint) 메서드가 있는지 찾고, 있다면 메서드를 호출. Symbol.toPrimitive는 시스템 심볼로, 심볼형 키로 사용.
  2. 1에 해당하지 않고 hint가 'string'이라면, obj.toString() 이나 job.valueOf() 호출
  3. 1, 2에 해당하지 않고, hint가 'number'나 'default'라면 obj.valueOf() 나 obj.toString() 호출
let user = {
    name: 'kim',
    money: 1000,

    [Symbol.toPrimitive](hint) {
        alert(`hint: ${hint}`);
        return hint == "string" ? `{name: "${this.name}"}` : this.money;
    }
};

alert(user); // hint: string -> {name: 'kim'}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500

toString과 valueOf

객체에 Symbol.toPrimitive가 없으면 자바스크립트는 아래 규칙에 따라 toString이나 valueOf를 호출

  • hint가 'string'인 경우: toString -> valueOf 순
  • 그 외: valueOf -> toString 순
  • toString은 문자열 [object Object] 반환
  • valueOf는 객체 자신 반환.
let user = {
    name: 'kim',
    money: 100,
    toString () {
      return `{name: "${this.name}"}`  ;
    },
    valueOf() {
        return this.money;
    }
};

alert(user); // toString -> {name: kim}
alert(+user); // valueOf -> 100
alert(user + 500); // valueOf -> 600