본문 바로가기
  • soldonii's devlog
Javascript 공부/TIL

200108(수) : 프로토타입, 객체지향 프로그래밍

by soldonii 2020. 1. 9.

오늘 배운 내용은 프토토타입과 객체지향 프로그래밍이다. 본 글에서는 객체, 프로토타입 등에 대해서 공부한 내용을 정리하려 한다.

 

1. 객체란 무엇인가?

  • 흔히 '자바스크립트에서는 모든 것이 객체다'라는 말을 하는데, 명백하게 사실과 다른 말이다.
  • string, number, boolean, null, undefined와 같은 단순 원시 타입은 객체가 아니다.
  • 다만 함수배열의 경우에는 '복합 원시 타입'이라는 객체의 하위 타입의 한 종류이다.

 

# 내장 객체

내장 객체 또한 객체의 하위 타입이다. 이들은 자바스크립트의 타입 중 하나처럼 보이지만 사실은 단지 자바스크립트에 내장된 함수일 뿐이고, 각각 생성자 함수로 사용되어서 주어진 하위 타입의 새로운 객체를 생성하는 역할을 한다.

  • String, Number, Boolean, Object, Function, Array, Date, RegExp, Error

 

1) 객체와 프로퍼티

  • 객체에 값을 보관하면 마치 객체 내부에 프로퍼티 값들이 보관되는 것 같지만 겉보기에만 그렇고 실제로는 그렇지 않다.
  • 실제로는 객체 컨테이너에는 각 프로퍼티 값이 있는 곳을 가리키는 포인터로써 역할하는 프로퍼티명이 담겨있게 된다.
  • 객체의 프로퍼티명은 언제나 문자열이다. 숫자를 쓰던, 배열을 쓰던 프로퍼티명에는 문자열만 담기게 된다.

 

2) 객체 복사

  • 얕은 복사의 경우 ES6부터 제공되는 Object.assign()을 이용할 수 있다. (Object.assign MDN)
  • 다만 얕은 복사는 원시값은 그대로 복사되지만, 참조값은 원래 객체의 레퍼런스와 같은 대상을 가리키는 또 다른 레퍼런스를 만들게 된다.
let array = [], obj = {};
function func () {};
let myObject = {
  a: 2,
  b: array,
  c: obj,
  d: func
};

let newObject = Object.assign({}, myObject);
newObject.a; //2
newObject.b === array; // true
newObject.c === obj; // true
newObject.d === func; // true, 원래 레퍼런스와 같은 대상을 가리키는 또 다른 레퍼런스이기 때문에 true이다.
  • 깊은 복사는 JSON을 이용할 수 있다.(let newObject = JSON.parse(JSON.stringify(myObject));)

 

3) 프로퍼티 서술자

객체 내의 모든 프로퍼티는 프로퍼티 서술자로 표현이 된다.

let myObject = {
  a: 2
};
Object.getOwnPropertyDescriptor(myObject, 'a');
// {
// value: 2,
// writable: true,
// enumerable: true,
// configurable: true
// }
  • 만약 프로퍼티 값의 쓰기 가능여부를 조절하고 싶을 경우, writable를 조절한다.
Object.defineProperty(myObject, 'a', {
  value: 2,
  writable: false, // 쓰기 금지
  configurable: true,
  enumerable: true
});
myObject.a = 3;
myObject.a; // 2

 

 

  • 위처럼 writable 속성을 false로 바꾸면, 아래에 myObject.a = 3이라는 코드는 조용히 실패하게 된다. (엄격모드에서는 TypeError가 난다.)
  • configurable 설정이 true이면 위처럼 defineProperty를 이용해서 객체의 프로퍼티 서술자를 변경할 수 있다.
  • 다만 한 번 이를 false로 변경할 경우 절대 복구되지 않으니 유의해야 한다.
  • 또한 false일 경우, 해당 프로퍼티는 delete로 지워지지 않는다.
  • delete는 삭제 가능한 프로퍼티를 삭제하는 용도로만 사용되고, 만약 특정 객체/함수의 마지막 프로퍼티를 삭제할 경우, 레퍼런스가 삭제되면서 이 객체/함수는 아무것도 참조하지 않기 때문에 가비지 컬렉션의 대상이 된다.
  • enumerable의 경우, 이 속성을 false로 바꾸면, 해당 프로퍼티에 접근은 할 수 있지만, for ... in 루프 구문에서는 감춰진다.

 

4) [[Get]]

  • let myObject = { a: 2 }; 에서 myObject.a로 프로퍼티에 접근할 경우, a란 프로퍼티를 myObject에서 찾지 않는다.
  • 대신 myObject에 대해서 [[Get]] 연산을 수행한다. 이 연산은 주어진 이름의 프로퍼티를 찾아보고, 있으면 반환하고 없을 경우 다른 작업을 한다.

5) 객체 내의 존재 확인

let myObject = {
  a: 2;
};
console.log("a" in myObject); // true
console.log("b" in myObject); // false

myObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("b"); // false
  • 객체 내에 특정 프로퍼티가 존재하는지 확인하는 방법은 2가지 이다.
  • 첫번째는 in 연산자를 사용하는 방법이고, 두번째는 hasOwnProperty 메소드를 사용하는 것이다.
  • in 연산자는 프로토타입 체인을 통해 해당 객체 내에 프로퍼티가 존재하지 않을 경우, 상위 프로토타입으로 거슬러 올라가서 해당 프로퍼티가 존재하는지까지도 확인한다.
  • 반면 hasOwnProperty는 오로지 해당 객체에 프로퍼티가 존재하는지에 대해서만 확인한다.
  • 객체에서 해당 프로퍼티를 찾지 못할 경우, undefined를 리턴한다.

 

2. 프로토타입

  • 자바스크립트의 객체는 [[Prototype]] 이라는 내부 프로퍼티가 존재하고, 다른 객체를 참조하는 레퍼런스로 사용이 된다.
  • 프로토타입 체인이 연결되면 특정 객체에서 프로퍼티를 찾지 못할 경우, 상위 프로토타입체인으로 거슬러 올라가면서 찾게 되고, 최상위까지 올라갔을 때에도 찾지 못하면 undefined를 호출한다.
  • 이는 for ... in loop에서도 마찬가지로 적용된다.
  • 모든 함수는 prototype 객체를 가지고 태어나며 모든 prototype 객체 내부에는 항상 constructor 프로퍼티가 존재한다. 이 둘은 서로가 서로를 가리키는 관계이다.
  • 생성자 함수 키워드 new와 함께 함수를 실행할 경우 '생성자 함수'라고 부르는데, 이렇게 생성된 대상을 '인스턴스'라고 부르며 '인스턴스'는 본인을 생성한 생성자 함수 내에 존재하는 prototype 객체를 상속받게 된다.
function Func () {};
Func.prototype; // Func 함수 내에 존재하는 prototype 객체.
Func.prototype.constructor; // prototype 객체 내에 존재하는 constructor 프로퍼티. Func를 가리킨다.

const instanceOfFunc = new Func();
// instanceOfFunc는 Func 생성자 함수의 인스턴스이다. Func.prototype을 상속받는다.

 

3. OOP

객체 지향 프로그래밍의 정의부터 살펴보자.

OOP is a programming paradigm organized around objects rather than actions and logic.
Object can contain related data and code, which represent information about the thing you are trying to model, and functionality or behavior that you want it to have.
만들고자 하는 대상에 대한 속성과 기능을 한 곳에 모아놓은 것이 바로 객체이다.

객체 지향 프로그래밍의 경우 하나의 객체 내에 만들고자 하는 대상과 관련된 모든 내용이 모아져 있다는 측면에서 이해하기 쉽다.

 

# 은닉화(Encapsultaion)

그런데 자칫 global scope에서 객체를 생성할 경우, 누구나 해당 객체에 대한 접근이 가능하고, 따라서 의도치 않게 해당 객체의 프로퍼티나 메소드가 변경될 수 있다. 이러한 문제를 방지하기 위한 방법 중 하나로 은닉화(캡슐화, Encapsulation)를 하게 되는데, 대표적으로 IIFE(Immediately Invoked Function Expression)을 활용할 수 있다.

 

IIFE(즉시호출 함수) 내에 객체의 프로퍼티들을 설정할 경우, 해당 객체의 프로퍼티는 global scope가 아닌 익명함수의 scope에 갇히기 때문에 global에서는 해당 프로퍼티에 접근할 수 없다. 단지 IIFE 함수가 return하는 것을 사용하는 것만 가능해진다. 이로써 객체를 더 안전하게 보관할 수 있다. 조금 더 자세한 내용은 이 글에 정리되어 있다.

 

# 추상화

  • 추상화는 복잡한 작동원리는 가리고 단지 이용자가 원하는 결과만을 노출시키는 것을 의미한다.

# Factory 함수

  • In JavaScript, any function can return a new object. When it’s not a constructor function or class, it’s called a factory function.
  • 즉 생성자 함수나 클래스가 아닌 함수 중 객체를 리턴하는 함수를 factory function이라고 부른다.

# Object.create

  • Object.create는 특정 대상 객체(ex. a 객체)에 상속해주길 원하는 객체(ex. b 객체)를 인자로 전달하여, 상속시켜준다.
  • 예를 들어 const objA = Object.create(objB); 라고 할 경우, objAobjB를 상속받는 빈 객체로 할당이 된다.

댓글