JS - Object

chu·2021년 5월 11일
0
post-thumbnail

프로젝트 경험을 하면서 "나는 얼마나 내가 작성한 코드에 대해서 알고 있을까?" 라는 의심을 갖게 됐다.

의심을 하게 된 이유는 이제 취업을 하고 싶기 때문에 여러 취업 후기, 구인 공고, 채용 인터뷰 등 보면서 쇠파이프로 정강이를 맞는 팩폭으로 세상은 쉽지 않다는 걸 다시 한번 깨닫고, 의심을 해소하고 풀기 위해 웹 개발자의 기본기인 자바스크립트부터 쭈욱 정리를 하며 되돌아보자.

정말 괜찮은 자바스크립트 정리 사이트를 발견했다. 그 내용 토대로 정리하며 한번 더 익히고, 마지막에 출처를 남기도록 하겠다.

난 내 머리를 과대평가 하지 않기 때문에! 기억보단 기록하자!


Object - 객체

자바스크립트는 객체(object) 기반의 스크립트 언어이며 자바스크립트를 이루고 있는 거의 “모든 것”이 객체이다. 원시 타입(Primitives)을 제외한 나머지 값들(함수, 배열, 정규표현식, 객체 리터럴 등)은 모두 객체이다.

자바스크립트의 객체는 키(key)과 값(value)으로 구성된 프로퍼티(Property) 들의 집합이다. 프로퍼티의 값으로 자바스크립트에서 사용할 수 있는 모든 값을 사용할 수 있다.

자바스크립트의 함수는 일급 객체이므로 값으로 취급할 수 있다. 따라서 프로퍼티 값으로 함수를 사용할 수도 있으며 프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메소드 라 부른다.

Property

프로퍼티는 프로퍼티 키(이름)와 프로퍼티 값으로 구성된다. 프로퍼티는 프로퍼티 키로 유일하게 식별할 수 있다. 즉, 프로퍼티 키는 프로퍼티를 식별하기 위한 식별자(identifier)다. 프로퍼티 키의 명명 규칙과 프로퍼티 값으로 사용할 수 있는 값은 아래와 같다.

  • 프로퍼티 키 : 빈 문자열을 포함하는 모든 문자열 또는 symbol 값
  • 프로퍼티 값 : 모든 값

프로퍼티 키에 문자열이나 symbol 값 이외의 값을 지정하면 암묵적으로 타입이 변환되어 문자열이 된다. 이미 존재하는 프로퍼티 키를 중복 선언하면 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어쓴다. 배열과는 달리 객체는 프로퍼티를 열거할 때 순서를 보장하지 않는다.

sybol(심볼) 이 무엇? 여기서 참고 해보자.

Method

프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메소드라 부른다.
즉, 메소드는 객체에 제한되어 있는 함수를 의미한다.

흔히 자주 쓰이는 배열에서의 내장 함수를 생각하면 좋을 것 같다.
reduce filter push unshipt 등..

객체 리터럴

일반적으로 쓰이는 객체 생성 방법이다.
중괄호{}를 사용하여 객체를 생성하는데 {} 내에 1개 이상의 프로퍼티를 기술하면 해당 프로퍼티가 추가된 객체를 생성할 수 있다. {} 내에 아무것도 기술하지 않으면 빈 객체가 생성된다.

let person = {
    name: 'Lee',
    gender: 'male',
    sayHello: function () {
        return 'Hi! My name is ' + this.name;
    }
};

console.log(typeof person); // object
console.log(person); // {name: "Lee", gender: "male", sayHello: ƒ}

많이 아는 내용이겠지만 객체와 객체는 false를 출력한다. 아래 예시를 보자.

console.log({} === {}); // false
console.log([] === []); // false

const obj1 = {};
const obj2 = {};

console.log(obj1 === obj2); // false

// true로 하기 위해서는 참조를 해야한다.

const obj3 = obj1; // obj1을 참조

console.log(obj1 === obj3); // true

이 이유는 메모리 관점에서 알고 넘어가야 한다!
빈 객체를 생성하게 되면 메모리1에서 생성이 된다고 생각을 하자. 또 한번 빈 객체를 생성하면 메모리2에서 생성이 되기 때문에 서로 다른 메모리를 바라보고 있다. 그렇기 때문에 true가 아닌 false다.

여기서 obj2는 obj1의 객체 즉,메모리1을 참조하기 때문에 true가 된다.

하지만 원시타입에 관해서는 다르게 적용 된다. 원시타입에서는 참조를 했다고 해서 무조건 true는 아니다. 이 부분은 따로 찾아보면 좋을 것 같다. (내용이 너무 길어져요 ㅠ)

위 메모리 내용은 코어 자바스크립트 라는 책을 보다가 알게 된 내용이다.
그림까지 있어서 보기 편했고, 이해하기도 쉬웠다.

Object 생성자 함수

new 연산자와 Object 생성자 함수를 호출하여 빈 객체를 생성할 수 있다. 빈 객체 생성 이후 프로퍼티 또는 메소드를 추가하여 객체를 완성하는 방법이다.

생성자(constructor) 함수란

  • new 키워드와 함께 객체를 생성하고 초기화하는 함수를 말한다. 생성자 함수를 통해 생성된 객체를 인스턴스(instance)라 한다.

  • 자바스크립트는 Object 생성자 함수 이외에도 String, Number, Boolean, Array, Date, RegExp 등의 빌트인 생성자 함수를 제공한다.

  • 일반 함수와 생성자 함수를 구분하기 위해 생성자 함수의 이름은 파스칼 케이스(PascalCase)를 사용하는 것이 일반적이다.

  • 객체가 소유하고 있지 않은 프로퍼티 키에 값을 할당하면 해당 객체에 프로퍼티를 추가하고 값을 할당한다. 이미 존재하는 프로퍼티 키에 새로운 값을 할당하면 프로퍼티 값은 할당한 값으로 변경된다.

파스칼 케이스

첫 글자와 중간 글자들이 대문자인 경우 파스칼 언어의 표기법과 유사하다고 하여 파스칼 케이스라고 한다.

아래 예시를 보고 어떻게 생성하고, 프로퍼티를 추가하는지 알아보자.

// 빈 객체의 생성
let person = new Object(); // Object 생정자 함수

// 프로퍼티 추가
person.name = 'Lee';
person.gender = 'male';
person.sayHello = function () {
  console.log('Hi! My name is ' + this.name);
};

console.log(typeof person); // object
console.log(person); // {name: "Lee", gender: "male", sayHello: ƒ}

person.sayHello(); // Hi! My name is Lee

위 처럼 Object 생성자 함수를 사용해서 객체를 생성할 수 있다. 하지만 일반적으로 자바스크립트에서는 객체 리터럴 방식으로 객체를 생성한다.

사실 객체 리터럴 방식으로 생성된 객체는 결국 빌트인(Built-in) 함수인 Object 생성자 함수로 객체를 생성하는 것을 단순화시킨 축약 표현(short-hand) 이다.

다시 말해, 자바스크립트 엔진은 객체 리터럴로 객체를 생성하는 코드를 만나면 내부적으로 Object 생성자 함수를 사용하여 객체를 생성한다. 따라서 개발자가 일부러 Object 생성자 함수를 사용해 객체를 생성해야 할 일은 거의 없다.

하지만 끝은 아니다! 또 다른 예제를 알아보자.

let person1 = {
  name: 'Lee',
  gender: 'male',
  sayHello: function () {
    console.log('Hi! My name is ' + this.name);
  }
};

let person2 = {
  name: 'Kim',
  gender: 'female',
  sayHello: function () {
    console.log('Hi! My name is ' + this.name);
  }
};

위 처럼 같은 형태의 객체를 만들 경우에 누가 봐도 반복작업 아닌가요? 강의를 들으면서 코드가 반복적으로 일어날 경우 의심을 해야한다고 들었다. 공통되는 코드를 찾아서 줄여야 한다는 생각!

그럼 위 상황을 생성자 함수로 줄여보자!

// 생성자 함수
function Person(name, gender) {
  this.name = name;
  this.gender = gender;
  this.sayHello = function(){
    console.log('Hi! My name is ' + this.name);
  };
}

// 인스턴스의 생성
let person1 = new Person('Lee', 'male');
let person2 = new Person('Kim', 'female');

생성자 함수를 사용하면 마치 객체를 생성하기 위한 템플릿(클래스)처럼 사용하여 프로퍼티가 동일한 객체 여러 개를 간편하게 생성할 수 있다.

  • 생성자 함수 이름은 일반적으로 대문자로 시작한다. 이것은 생성자 함수임을 인식하도록 도움을 준다.
  • 프로퍼티 또는 메소드명 앞에 기술한 this는 생성자 함수가 생성할 인스턴스(instance)를 가리킨다.
  • this에 연결(바인딩)되어 있는 프로퍼티와 메소드는 public(외부에서 참조 가능)하다.
  • 생성자 함수 내에서 선언된 일반 변수는 private(외부에서 참조 불가능)하다. 즉, 생성자 함수 내부에서는 자유롭게 접근이 가능하나 외부에서 접근할 수 없다.

마지막 두 가지에 대한 내용은 아래와 같이 설명할 수 있다.

function Person(name, gender) {
  let married = true;         // private
  this.name = name;           // public
  this.gender = gender;       // public
  this.sayHello = function(){ // public
    console.log('Hi! My name is ' + this.name);
  };
}

let person = new Person('Lee', 'male');

console.log(typeof person); // object
console.log(person); // Person { name: 'Lee', gender: 'male', sayHello: [Function] }

console.log(person.gender);  // 'male'
console.log(person.married); // undefined - 접근 불가

객체 프로퍼티 접근

프로퍼티 키

프로퍼티 키는 일반적으로 문자열(빈 문자열 포함)을 지정한다. 프로퍼티 키에 문자열이나 symbol 값 이외의 값을 지정하면 암묵적으로 타입이 변환되어 문자열이 된다. 또한 문자열 타입의 값으로 수렴될 수 있는 표현식도 가능하다.

하지만 자바스크립트에서 사용 가능한 유효한 이름인 경우, 따옴표를 생략할 수 있다. 반대로 말하면 자바스크립트에서 사용 가능한 유효한 이름이 아닌 경우, 반드시 따옴표를 사용하여야 한다.

따옴표('', "")를 사용하는 상황

const obj = {
  name: 'hyun uk',
  na_me: 'hyun uk',
  'na-me': 'hyun uk', // -는 연산자 표현이기 때문에 따옴표를 써야한다.
  gender: 'male',
  1: 10,
  function: 1 // OK. 하지만 예약어는 사용하지 말아야 한다. ex) if else while for...
}

프로퍼티 키 na-me에는 반드시 따옴표를 사용해야 하지만 na_me에는 생략 가능하다. na-me은 자바스크립트에서 사용 가능한 유효한 이름이 아니라 ‘-‘ 연산자가 있는 표현식이기 때문이다.

만약 위 규칙을 지키지 않으면 어떤 에러가 발생할까?

const person = {
  na-me: 'hyun uk', // SyntaxError: Unexpected token -
};

프로퍼티 값 읽기

객체의 프로퍼티 값에 접근하는 방법은 마침표(.) 표기법과 대괄호([]) 표기법이 있다. 예제를 통해 이 두 방법의 차이를 살펴보자.

const person = {
  'first-name': 'hyun-uk',
  'last-name': 'choi',
  gender: 'male',
  1: 10
};

console.log(person.first-name);    // NaN: undefined-undefined
console.log(person[first-name]);   // ReferenceError: first is not defined
console.log(person['first-name']); // 'hyun-uk'

console.log(person.gender);    // 'male'
console.log(person[gender]);   // ReferenceError: gender is not defined
console.log(person['gender']); // 'male'

console.log(person['1']); // 10
console.log(person[1]);   // 10 : person[1] -> person['1'] 변경 후 10 출력
console.log(person.1);    // SyntaxError

프로퍼티 키가 유효한 자바스크립트 이름이고 예약어가 아닌 경우 프로퍼티 값은 마침표 표기법, 대괄호 표기법 모두 사용할 수 있다.

프로퍼티 이름이 유효한 자바스크립트 이름이 아니거나, 예약어인 경우 프로퍼티 값은 대괄호 표기법으로 읽어야 한다. 대괄호([]) 표기법을 사용하는 경우, 대괄호 내에 들어가는 프로퍼티 이름은 반드시 문자열이어야 한다.

객체에 존재하지 않는 프로퍼티를 참조하면 undefined를 반환한다.

프로퍼티 값 수정 및 동적 생성

수정(갱신)

객체가 소유하고 있는 프로퍼티에 새로운 값을 할당하면 프로퍼티 값은 갱신된다.

동적 생성

객체가 소유하고 있지 않은 프로퍼티 키에 값을 할당하면 하면 주어진 키와 값으로 프로퍼티를 생성하여 객체에 추가한다.

const person = {
  'first-name': 'hyun-uk',
  'last-name': 'choi',
  gender: 'male',
  1: 10
};

// 수정
console.log(person.gender); // male
person.gender = female; // 수정
console.log(person.gender); // female

// 동적 생성
console.log(person.age); // undefinde
person.age = 100; // age 프로퍼티 key, value 추가
console.log(person.age); // 100

프로퍼티 삭제

delete 연산자를 사용하면 객체의 프로퍼티를 삭제할 수 있다. 이때 피연산자는 프로퍼티 키이어야 한다.

const person = {
  'first-name': 'hyun-uk',
  'last-name': 'choi',
  gender: 'male',
  1: 10
};

delete person['first-name'];
console.log(person['first-name']); // undefined

delete person;
console.log(person); // Object {first-name: 'hyun uk', last-name: 'choi'}

객체 순회

배열이나 문자열 '열'이라는 건 무언가 나열이 되었다. 라는 뜻으로 배열 내장 함수를 사용해서 순회를 돌며 여러 작업을 할 수 있다. 객체에서는 어떤 방법으로 순회를 할 수 있을까?

for in 문

for-in 문을 사용하면 객체(배열 포함)에 포함된 모든 프로퍼티에 대해 루프를 수행할 수 있다.

const person = {
  'first-name': 'hyun-uk',
  'last-name': 'choi',
  gender: 'male',
  age: 10
};

// i - 객체의 프로퍼티 키
for (let i in person) {
  console.log(i +': '+ person[i]);
}

// 아래 결과
// age: 10
// first-name: hyun-uk
// last-name: choi
// gender: male

결과가 이상하다고 생각이 들어야 한다. 실제 객체를 생성해서 넣은 프로퍼티와는 순서가 다르다.
객체의 경우, 프로퍼티의 순서가 보장되지 않는다. 그 이유는

  • 원래 객체의 프로퍼티에는 순서가 없기 때문이다.
  • 배열은 순서를 보장하는 데이터 구조이지만 객체와 마찬가지로 순서를 보장하지 않는다.
  • 배열 요소들만을 순회하지 않는다.(?? 아래 예시를 참고하자)
const arr = ['가', '나'];
arr.name = 'hyun uk';

for (let i in arr) {
  console.log(i +': '+ arr[i]);
}

// 0: '가'
// 1: '나'
// name: 'hyun uk' - index가 아닌 name 그대로 출력이 된다.

이와 같은 for-in 문의 단점을 극복하기 위해 ES6에서 for-of 문이 추가되었다.
자세한 사용 방법은 MDN에서 참고.

const arr = ['가', '나'];
arr.name = 'hyun uk';

for (let i of arr) {
  console.log(i);
}

// 가
// 나

for (let [index, value] of arr.entries()) {
    console.log(index, value);
};

// 0 가
// 1 나

결론 : for–in 문은 객체의 프로퍼티를 순회하기 위해 사용하고 for–of 문은 배열의 요소를 순회하기 위해 사용한다.

이 이후에는 메모리 관점 이야기가 있었다. 아는 내용이었기 때문에 제외하고 이렇게 자바스크립트에 대한 기본 Object에 대해서 정리를 하였다. 이 외에 많은 내용은 있지만 다음 시간으로 정리를 하겠다.

다시 한번 집고 넘어가니 솔직히 몰랐던 부분도 있었지만, 다음부터는 코드를 신중하게 작성해야 겠다는 생각이 들었다.

출처는 https://poiemaweb.com/

여기

profile
한 걸음 한걸음 / 현재는 알고리즘 공부 중!

0개의 댓글