게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌19 - JavaScript - 8년 전 등록 / 4년 전 수정

Object 객체

안녕하세요. 중급 강좌 첫 번째 시간이네요! 자바스크립트는 객체 지향 프로그래밍 언어이니만큼 Object 객체에 대해 알아보겠습니다. Object가 객체이니까 Object 객체는 객체 객체이겠네요! 사실 Object 그 자체도 객체입니다. 모든 객체의 최종 prototype이기도 하죠.

예시를 하나 들어 객체의 prototype이 뭔지 볼까요? 이미 구현된 객체의 prototype을 확인할 때는 __proto__를 사용합니다.

Math.__proto__; // Object { ... }

네 바로 Object 객체입니다. 생성자를 통해 만든 객체의 prototype의 prototype도 Object 객체입니다.

function Person(name) { this.name = name; }
Person.prototype.sayHello = function() {
  alert(this.name);
};
var zero = new Person('zero');
zero.__proto__; // { sayHello: function() }
zero.__proto__.__proto__; // Object { ... }

그렇다면 Object 객체 자체의 prototype은 뭘까요?

Math.__proto__.__proto__; // null

네 없습니다. Object가 최종이기 때문이죠. Object 객체의 생성자는 window 객체에 저장되어 있습니다. 모든 객체가 Object 객체로부터 상속받기 때문에 모든 객체는 Object 객체의 메소드들을 사용할 수 있습니다.

Object

객체.hasOwnProperty(속성명)

객체의 속성이 상속받지 않은 속성인지 알려줍니다. 자신의 속성이면 true, 부모의 속성이거나 아예 속성이 아니면 false를 반환합니다.

var obj = {
  example: 'yes',
};
obj.example; // yes
obj.hasOwnProperty('example'); // true
obj.toString; // function toString() { [native code] }
obj.hasOwnProperty('toString'); // false

객체.isPrototypeOf(대상)

객체가 대상의 조상인지 알려줍니다.

var GrandParent = function() { };

var Parent = function() { };
Parent.prototype = new GrandParent();
Parent.prototype.constructor = Parent;

var Child = function() { };
Child.prototype = new Parent();
Child.prototype.constructor = Child;

var child = new Child();
Parent.prototype.isPrototypeOf(child); // true
GrandParent.prototype.isPrototypeOf(child); // true

Object.getPrototypeOf(객체), Object.setPrototypeOf(객체, prototype)

객체의 prototype을 조회하거나 설정할 수 있습니다.

Object.getPrototypeOf(child); // Parent
Object.getPrototypeOf(new GrandParent()); // 빈 객체 { }
Object.setPrototypeOf(child, new Parent());

instanceof

객체가 특정 생성자의 자식인지 조회할 수 있습니다.

child instanceof Parent; // true
child instanceof GrandParent; // true

객체.propertyIsEnumerable(속성)

해당 속성이 열거 가능한 속성인지 알려줍니다. 열거 가능이란 for ... in과 같은 반복문 안에서 쓸 수 있는지를 말합니다. 상속받은 속성과 해당 객체의 속성이 아닌 것은 기본적으로 제외됩니다.

var a = [false, 1, '2'];
a.propertyIsEnumerable(0); // true
a.propertyIsEnumerable('length'); // false
for (var value in a) {
  console.log(value); // 0, 1, 2
}

객체.toString

가끔 객체를 alert하거나 console.log할 때 원하는 결과는 안 나오고, [object Object] 이런 게 나올 때가 있습니다. 내부적으로 toString 메소드가 호출된 결과입니다. 문자열끼리 더할 때 주로 호출됩니다. 기본적으로는 객체의 종류를 알려주고, 사용자가 임의로 바꿀 수 있습니다.

var obj = { a: 'hi', b: 'zero' };
obj.toString(); // [object Object]
Math.toString(); // [object Math]
obj.toString = function() {
  return this.a + ' ' + this.b;
}; // 임의로 바꿈
obj.toString(); // 'hi zero';
obj + ' cho'; // 'hi zero cho'

객체.valueOf

객체의 기본 값을 의미합니다. 숫자 계산을 할 때 내부적으로 호출됩니다. toString처럼 내부적으로 호출되기 때문에 관리하기 어렵습니다.

var obj = { a: 'hi', b: 'zero' };
obj.valueOf(); // { a: 'hi', b: 'zero' }
obj + 5; // '[object Object]5' <-- 내부적으로 toString이 호출됨
obj.valueOf = function() {
  return 3;
}
obj + 5; // 8 <-- 내부적으로 valueOf가 호출됨

Object.create(prototype, 속성들)

객체를 생성하는 방법 중 하나입니다. 속성들 부분은 writable, configurable, enumerable, get, set ,value의 옵션이 있는데 아래 defineProperties를 참고하세요.

var obj = {}; // Object.create(Object.prototype); 과 같음
var obj2 = Object.create(null, {
  a: {
    writable: true,
    configurable: false,
    value: 5,
  }
});
obj2.a; // 5

Object.defineProperties(객체, 속성들), Object.defineProperty(객체, 속성, 설명)

객체의 속성을 자세하게 정의할 수 있습니다. 속성의 설명을 따로 설정할 수 있는데, writable은 속성값을 변경할 수 있는지, enumerable은 for ... in 반복문 안에서 사용할 수 있는지, configurable은 속성의 설명을 바꿀 수 있는지를 설정합니다. false인 경우 delete 동작도 불가능합니다. 기본적으로 writable, enumerable, configurable은 false입니다. value는 속성의 값, get은 속성의 값을 가져올 때, set은 속성의 값을 설정할 때를 의미합니다.

var obj = {};
Object.defineProperties(obj, {
  a: {
    value: 5,
    writable: false,
    enumerable: true,
  },
  b: {
    get: function() {
      return 'zero';
    },
    set: function(value) {
      console.log(this, value);
      this.a = value;
    },
    enumerable: false,
    configurable: false,
  },
});
obj.a; // 5
obj.b; // 'zero'
obj.a = 10;
obj.a; // writable이 false라 그대로 5
for (var key in obj) {
  console.log(key); // b의 enumerable이 false이니까 a만 log됨
}
obj.b = 15; // 15로 설정되는 대신 set의 내용이 실행됨. set의 value는 15
obj.a; // this.a = value로 인해 15로 바뀌어야 하나 writable이 false라 무시됨
obj.b; // 그대로 'zero'
Object.defineProperty(obj, 'b', {
  value: 5
}); // Uncaught TypeError: Cannot redefine property: b

마지막은 configurable이 false기 때문에 b 속성의 설명을 재정의할 수 없습니다.

Object.defineProperty(obj, 'c', {
  value: { x: 3, y: 4 },
  writable: false,
  enumerable: true,
});
obj.c; // { x: 3, y: 4 }
obj.c = 'zero';
obj.c; // writable이 false라 그대로 { x: 3, y: 4 }
obj.c.x = 5; // 값이 객체인 경우 그 객체의 속성을 바꿈
obj.c; // { x: 5, y: 4 }로 바뀜

writable은 속성 값을 바꾸는 것을 막지만 만약 속성의 값이 객체인 경우에는 그 객체 안의 속성을 바꾸는 것은 막지 못합니다. 바꾸는 것을 전체적으로 막기 위해서 Object.freeze 메소드가 있습니다.

Object.getOwnPropertyDescriptor(객체, 속성)

속성의 설명 값을 불러옵니다. 위의 예시를 그대로 사용해보겠습니다.

Object.getOwnPropertyDescriptor(obj, 'b'); // { enumerable: false, configurable: false, get: function() {}, set: function(value) {} }

Object.freeze, Object.seal, Object.preventExtensions

위의 예시에서 writable을 false로 해도, value가 객체인 경우에는 객체의 속성을 바꾸는 것을 막지 못합니다. Object.freeze를 사용하면 객체 전체를 바꾸지 못하게 고정할 수 있습니다. 값도 못 바꿀뿐더러, 속성을 추가 또는 제거할 수도 없고, 속성의 설명을 바꿀 수도 없습니다.

var frozenObj = Object.freeze(obj);
frozenObj.a = 10;
frozenObj.a; // 그대로 5
delete frozenObj.c; // false
Object.freeze(obj.c); // 이것까지 해야 내부 객체까지 완전히 얼려짐

Object.seal의 경우는 속성의 추가, 제거를 막고, configurable을 false로 바꿉니다. 대신 속성의 값은 writable이 true이기만 하면 바꿀 수 있습니다.

var sealedObj = Object.seal(obj);
sealedObj.a = 10;
sealedObj.a; // 5로 변경이 안 되지만 writable이 true면 변경 가능
delete sealedObj.c; // false

그냥 속성의 추가만 막고 싶다면 Object.preventExtensions가 있습니다. 그 외의 속성 제거, 값 변경, 설정 변경은 가능합니다.

var nonExtensible = Object.preventExtensions(obj);
nonExtensible.d = 'new';
nonExtensible.d; // undefined

Object.keys

객체의 속성명을 모두 가져와 배열로 만듭니다. enumerable이 false인 것은 빠집니다.

Object.keys(obj); // ['a', 'c']

Object.isFrozen, Object.isSealed, Object.isExtensible

객체가 freeze 되었는지, sealed 되었는지 또는 preventExtension 상태인지 알려줍니다.

Object.isFrozen(frozenObj); // true
Object.isSealed(sealedObj); // true
Object.isExtensible(nonExtensible); // false

typeof

식의 타입을 알려줍니다. 배열과 null도 object로 표시되기 때문에 배열을 구분하려면 Array.isArray 메소드를 사용하고, null을 구분하려면 따로 처리해야합니다. null이 object로 표시되는 것은 흔히 자바스크립트의 실수라고 여겨집니다.

var a = 1;
var b = 'zero';
var c = true;
var d = {};
var e = [];
var f = function() {};
var g;
var h = null;

typeof a; // 'number'
typeof b; // 'string';
typeof c; // 'boolean';
typeof d; // 'object';
typeof e; // 'object';
typeof f; // 'function';
typeof g; // 'undefined'
typeof h; // 'object';

delete

객체 내의 속성을 지울 수 있습니다. 성공하면 true를 실패하면 false를 반환합니다. configurable이 false거나 freeze된 상태면 실패합니다.

var obj = {
  a: 'hi',
  b: 'zero',
};
obj.b; // zero
delete obj.b;
obj.b; // undefined

후... 오늘 많은 것을 배웠습니다. 꼭 복습하셔야 합니다. 이제 객체의 속성은 마스터한 겁니다. 다음 시간에는 자바스크립트 함수의 범위에 대해 알아보겠습니다.

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

7개의 댓글이 있습니다.
4년 전
안녕하세요?

강의를 순서대로 보면

Object.getPrototypeOf(child); // GrandParent

여기 주석 // GrandParent -> Parent여야하지 않나요?

감사합니다.
4년 전
그렇네요. 감사합니다.
6년 전
Child {}
__proto__: GrandParent
__proto__: GrandParent
__proto__:
constructor: ƒ GrandParent()
__proto__: Object

왜 child의 __proto__가 Parent가 아니고 GrandParent로 보여질까요?
Parent.prototype.isPrototypeOf(child); // true
GrandParent.prototype.isPrototypeOf(child); // true
인걸보면 잘못 설정된 것도 아닌것 같은데 ㅜ.ㅜ
6년 전
아 코드 수정했습니다. Parent.prototype.constructor = Parent; 이 부분을 넣어주면 정상 작동합니다. 이 부분을 넣는 이유는 객체 상속 강좌에서 자바스크립트의 버그를 고치기 위함이라고 설명드렸습니다.
6년 전
typeof 가 객체의 타입을 알려준다고 하셨는데 var a = 1; 일때 a도 객체의 범주안에 포함되는건가요? 그렇다면 자바스크립트에서는 프리미티브 타입이라는게 존재하지않고 다 래퍼인건지 궁금하네요.
6년 전
숫자, 문자열, 불린은 프리미티브입니다.
6년 전
그렇다면 typeof 가 객체의 타입을 알려준다는 말은 잘못된 말 아닌가요? var a = 1; 여기서 a 는 객체가 아니지않나요?
6년 전
식의 타입으로 수정했습니다.
6년 전
Object.preventExtensions 을 설명해주신 부분에 '설정 변경'이라는 말은 configurable 변경을 뜻하는 것인가요??
6년 전
네 맞습니다.
7년 전
확실히 중급과정이라 어렵네요..
1. var sealedObject = Object.seal(obj);
sealedObj.a = 10;
sealedObj.a; // 10으로 변경됨
이 부분에서 sealedObj가 오타같습니다. 에러가 나길래..
sealedObject.a = 10; 로 바꿔서 써보니까 에러는 안나는데 이번에는 또
sealedObject.a; 이게 주석처럼 10으로 변경되지는 않더라구요..ㅠ 5로 반환됩니다.

2. Object.isExtensible(nonExtensible); // true
이것도 false로 반환하던데요.
아래는 코드는 잘 됐는데 뭐가 문제인지 잘 모르겠네요...ㅠ
var nonExtensible = Object.preventExtensions(obj);
nonExtensible.d = 'new';
nonExtensible.d; // undefined
7년 전
오타 두 개를 찾아주셔서 감사합니다. 1번은 5고, 2번은 false가 맞습니다.
7년 전
Object.seal() 메소드 예제에 보시면 선언한 변수랑 사용하는 변수랑 다르네요. 제가 잘못 이해한게 아니라면 혹시 오타가 아닌가 싶네요ㅋ
7년 전
맞는 코드입니다. 위의 예제랑 이어지는 거에요.
7년 전
그렇군요!
7년 전
다시 복습해야겠네요
8년 전
제가 직접 타이핑해서 실행해보기도하고, 복붙해서 실행해보기도 했는데 브라우저에 아무것도 안나와서...뭔가 잘못된걸까요?
8년 전
고쳤습니다. 이제 다시 복사하시면 될 거에요. 죄송하네요 ㅠㅠ 저도 공부하는 학생이라 코드도 오류가 많고 서버도 자주 터집니다... 그래도 항상 제 글 봐주시고 오류 잡아주셔서 감사합니다!
8년 전
저는 설명해주셔도..몇번씩 다시봐야 하는데 이런 코드를 짜실 수 있다는게 대단하신거같아요 ㅠㅠ 많이배워가겠습니다! 좋은자료 항상 감사해요~!
8년 전
사실 코드보다 알고리즘이 더 중요합니다. 제가 이번 학기에 알고리즘에 대해 배운 후 해당 강좌도 올려볼게요.