티스토리 뷰
자스의 기본 데이터 타입은 객체다. 객체는 일종의 복합체로, 여러 값들을 묶어 이름으로 저장하고, 값을 가져올 수 있다. 다시 말해 객체는 이름과 값으로 구성된 프로퍼티들의 정렬되지 않은 집합이다. 객체는 문자열과 값이 연결된 집합이라고 할 수 있다. 뿐만 아니라 객체는 객체가 가진 고유 프로퍼티를 유지하는 것 외에 프로토타입이라고 하는 다른 객체의 프로퍼티를 상속받는다. 객체의 메서드들은 일반적으로 상속받은 프로퍼티이고, 이를 프로토타입 상속이라고 한다.
객체는 동적이지만 정적 객체나 정적 타입 언어에서의 구조체처럼 사용할 수도 있다. 객체는 객체 자체를 복사하지 않고 참조한다.(얇은 복사) 객체의 각 프로퍼티는 프로퍼티 속성이라고 하는 연관된 값을 갖는다.
1. 쓰기 속성은 프로퍼티 값의 수정 가능 여부를 결정한다.
2. 열거 속성은 프로퍼티의 이름은 for/in 루프에서 읽을 수 있는지 여부를 결정한다.
3. 설정 속성은 프로퍼티의 삭제 가능 여부와 프로퍼티 속성의 변경 여부를 결정한다.
객체의 프로퍼티 뿐 아니라, 모든 객체는 세 가지의 속성을 갖는다.
1. 객체의 prototype은 상속받은 프로퍼티들을 가진 객체의 대한 참조 변수다.
2. 객체의 class는 객체의 유형을 분류하는 문자열이다.
3. 객체의 extensible 플래그는 객체에 새 프로퍼티의 추가 여부를 결정한다.
네이티브 객체 : ECMAScript 명세에 정의된 객체이거나 객체의 클래스다. Array, Function, Date, 정규 표현식들은 전부 네이티브 객체다.
호스트 객체 : 브라우저와 같이 자바스크립트 인터프리터가 내장된 호스트 환경에 정의된 객체다. HTMLElement 객체는 웹 페이지의 구조가 클라이언트 측 자스로 표현된 호스트 객체다. 호스트 객체는 호트 환경에서 메서드들을 정의할 때 일반적으로 자스 함수 객체로 정의하는 것과 마찬가지로 네이티브 객체일 수도 있다.
사용자 정의 객체 : 자스 코드의 실행으로 생성된 객체다.
고유 프로퍼티 : 객체에 직접 코드로 정의한 프로퍼티다.
상속받은 프로퍼티 : 객체 프로토타입 객체에 의해 정의된 프로퍼티를 말한다.
객체 생성하기
객체는 객체 리터럴 또는 new 키워드를 이용해 생성될 수 있고, Object.create() 함수를 통해 생성될 수도 있다.
객체 리터럴
객체를 생성하는 가장 쉬운 방법
var empty={}; //프로퍼티가 없는 빈 객체
var point={x:0, y:0};
var point2={x:point.x, y:point.y+1};
var book={
"main title": "javascript",
author:{ //프로퍼티 author의 값은 객체 그 자체다.
firstname:David"
}
};
new를 사용해 객체 생성하기
new 연산자로 객체를 만들고, 초기화할 수 있다. new 키워드는 반드시 함수 호출 다음에 와야 한다. 이때 사용되는 함수를 생성자라고 하는데, 새로 생성된 객체를 초기화하는 역할을 한다. 코어 자스는 원시 타입을 위해 내장 생성자를 포함하고 있다.
var o=new Object();
var a=new Array();
var d=new Date();
var r=new RegExp("js"); // 패턴 매칭을 위한 RegExp 객체를 생성
프로토 타입
자스의 모든 객체는 두 번째 자스 객체와 연관되어 있다. 이 두 번째 객체는 프로토타입으로 알려져 있고, 이때 첫 번째 객체는 프로토타입으로부터 프로퍼티들을 상속받는다. 객체 리터럴로 생성된 모든 객체는 프로토타입 객체가 같으며, 자스코드에서 이 프로토타입 객체는 Object.prototype으로 참조할수 있다. new 키워드를 사용해 생성자를 호출하면 생성자의 프로토타입을 자신의 프로토타입으로 하는 객체가 생성된다. new Objecct()로 생성된 객체는 Ojbect.prototype을 상속받으며 {}로 생성된 객체와 동일하다. new Array()로 생성된 객체는 Array.prototype 이런식이다.
Object.prototype은 매두 드문 객체 중 하나로 prototype이 없다. 즉 아무런 프로퍼티도 상속받지 않는다. 모든 내장된 생성자 또는 사용자 정의 생성자는 프로토토타입을 갖는데 이때 Object.prototype을 상속 받는다. 예를들어 Date 객체는 new Date()를 통해 만들어지며, Object.prototype과 Date.prototype을 전부 상속한다. 이처럼 프로토타입 객체들의 연속된 연결을 프로토타입 체인이라고 한다.
Object.create()
ECMAScript 5에서는 객체를 생성하는 Object.create() 메서드를 지원한다. 이때 메서드의 첫 번째 인자로 프로토타입 객체를 전달할 수 있다. Object.create()는 새 객체의 프로퍼티 정보를 가진 객체를 두 번째 인자로 갖는데, 이 인자는 생략할 수 있다.
var o1=Object.create({x:1, y:2}); //o1은 x,y 프로퍼티를 상속 받는다.
var o2=Object.create(null); //o2에는 프로퍼티와 메서드가 없다.(toString도 안돼)
var o3=Object.create(Object.prototype); // {}, new Object와 같은 객체다
임의의 프로토타입으로 새 객체를 만들 수 있다는 점(특정 객체를 상속받는 상속자 객체를 만들 수 있다는 점)은 유용
프로퍼티 접근 및 설정
프로퍼티의 값을 가져오기 위해서는 마침표 연산자 또는 대괄호 연산자를 사용한다.
연관 배열로서의 객체
object.property
object["protperty"]
이러한 형태의 배열을 연관 배열이라고 하고, 해시나 맵, 사전이라고도 한다. 모든 자스 객체는 연관 배열이다.
마침표 연산자를 사용해 객체의 프로퍼티에 접근할 때는 프로퍼티의 이름이 반드시 식별자로 표현되어야 한다.
식별자는 프로그램이 실행될 때 변경할 수 없다.
반면에 []연산자를 사용해 객체의 프로퍼티에 접근할 때는 프로퍼티의 이름이 문자열로 표현된다. 문자열은 자스 데이터 타입이기 때문에 프로그램 실행 시점에 생성되고 중간에 변경될 수 있다.
var addr="";
for(i=0;i<4;i++){
addr+=customer["address"+i]+'\n';
[]연산자를 이용하면 함수에 전달해서 프로퍼티의 이름으로 사용할 수 있다.
function addstock(portfolio, stockname, shares){
portfolio[stockname]=shares;
}
상속
자스객체는 고유 프로퍼티들을 가지고 있고, 동시에 해당 객체의 프로토타입 객체로부터 여러 프로퍼티들을 상속받는다.
ex) 객체 o에서 프로퍼티 x를 찾는다고 하자. 객체 o가 프로퍼티 x를 갖고 있지 않으면, o의 프로토타입 객체에서 x를 찾는다. 만약 프로토타입 객체에 고유 프로퍼티 x가 없다면, 상위 객체의 프로토타입 객체에서 프로퍼티 x를 찾는다.
이러한 작업은 프로퍼티 x를 찾거나 해당 객체 또는 객체의 프로토타입 객체가 null이 될 때까지 계속된다. 이렇게 프로퍼티 체인 또는 연결 리스트를 생성한다.
var o={};
o.x=1;
var p=inherit(o);
p.y=2;
var q=inherit(p);
q.z=3;
var s=q.toString();
q.x+q.y;
//3
function inherit(p){
if(p==null) throw TypeError();
if(Object.create)
return Object.create(p);
var p=typeof p;
if(p!=="object" && t!=="function") throw TypeError();
function f(){};
f.prototype=p;
return new f();
}
상속받은 프로퍼티를 선택적으로 재정의할 수도 있다. 이때 상위 프로토타입 객체의 프로퍼티는 바뀌지 않는다.
프로퍼티 접근 에러
존재하지 않는 프로퍼티에 접근해도 에러가 발생하지 않는다.(undefined) book.subtitle
하지만 객체에 존재하지 않는 프로퍼티에 접근하려고 하면 에러가 발생한다. book.subtitle.length
이러한 예외를 막기위한 두 가지 방법이 있다.
//#1.구체적이고 확실한 방법
var len=undefined;
if(book){
if(book.subtitle)
len=book.subtitle.length;
}
//#2. 간단하고 관용적인 방법. subtitle.length 값 또는 undefined가 반환된다.
var len=book && book.subtitle && book.subtitle.length
신기하게도 다음 예제와 같은 경우에는 프로퍼티에 값을 설정할 수 없지만 어떠한 예외를 발생시키지 않는다.
Objecct.prototype=0; 이는 엄격한 모드에서 수정되었다.
프로퍼티 삭제하기
delete 연산자로 객체의 프로퍼티를 삭제한다. delete 연산자는 프로퍼티에 설정된 값과 무관하게 프로퍼티 자체가 객체에 존재할 때 동작한다. 상속받은 프로퍼티가 아닌 고유 프로퍼티만 지울 수 있다. delete 표현식은 삭제에 성공하거나, 프로퍼티가 존재하지 않아서 실패인 경우에나 항상 true이다. configurable 속성이 false인 프로퍼티를 지우지 않는다. 내장 객체의 특정 프로퍼티들은 속성을 변경할 수 없고 엄격한 모드에선 typeerror, 비 엄격한 모드에서는 false
프로퍼티 검사하기
객체는 프로퍼티 집합의 일종으로 생각할 수 있다.
in 객체에 해당 프로퍼티가 존재하면 true
var o={x:1};
"x" in o; //true
"y" in o; //false
"toString" in o; //true. 객체 o에 상속받은 프로퍼티 toString이 있기 때문에
hasOwnProperty 객체에 해당 프로퍼티가 존재하면 true(상속은 false)
var o={x:1};
o.hasOwnProperty("x"); //true
o.hasOwnProperty("y"); //false
o.hasOwnProperty("toString"); //false 상속받은건 안돼
propertyIsEnumerable() hasOwnProperty()로 테스트한 결과를 정제. 객체에 주어진 이름의 고유 프로퍼티가 존재하고, 열거할 수 있는(enumerable 속성이 true) 프로퍼티인 경우에만 true를 반환
var o=inherit({y:2});
o.x=1;
o.propertyIsEnumerable("x"); //true
Object.prototype.proertyIsEnumerable("toString"); //false
undefined가 아닌 경우 단순히 프로퍼티를 조회할 때는 in 연산자 대신 논리 연산자 !==를 사용하는 것이 효과적
프로퍼티 열거하기
객체가 가진 모든 프로퍼티를 순회하고 싶을 때 보통 for/in 루프를 사용
상속받은 내장 메서드는 열거할 수 없지만, 사용자가 임의로 추가한 프로퍼티들은 객체 내에서 열거할 수 있다.
for/in 루프를 비롯해 프로퍼티 이름을 열거하는 두가지 함수가 있다.
Object.keys() : 객체가 가진고유 프로퍼티 중에 열거할 수 있는 프로퍼티 이름을 배열에 담아 반환
Object.getOwnPropertyNames() : 해당 객체가 가진 모든 고유 프로퍼티의 이름을 배열로 반환
Getter와 Setter 프로퍼티
프로퍼티의 값을 getter/setter 메서드로 대체할 수 있다. 이때, getter/setter 메서드로 정의된 프로퍼티는 단순히 값을 갖는 데이터 프로퍼티와는 다른 접근자 프로퍼티라고 한다.
프로그램이 객체의 프로퍼티 값에 접근할 때, 자스 엔진은 getter 메서드를 아무런 인자 없이 호출한다. 이때, getter 메서드의 반환 값은 프로퍼티 접근 표현식의 값이 된다.
프로그램이 프로퍼티의 값을 변경할 때, 자스 엔진은 setter메서드를 호출한다. 이때 할당자(=)의 오른쪽에 있는 값을 setter 메서드의 인자로 전달한다. setter 메서드는 프로퍼티의 값을 설정하는 것을 담당하고, 메서드의 반환 값은 무시된다.
var o={
//데이터 프로퍼티
data_prop: value,
//한 쌍의 함수로 정의된 접근자 프로퍼티
get accessor_prop(){},
set accessor_prop(value){}
};
프로퍼티 속성
프로퍼티 이름과 그 값 뿐 아니라, 프로퍼티 그 자체를 결정하는 세 가지 속성 writable과 enumerable, configurable이 있다. writable 속성은 프로퍼티 값의 변경 여부를 결정한다. enumerable 속성은 프로퍼티가 열거될 수 있는지 여부를 결정하고, configurable 속성은 configurable 속성 뿐 아니라 writable 속성과 enumerable 속성 값의 변경 여부를 결정한다.
데이터 프로퍼티의 네 가지 속성은 value, writable, enumerable, configurable이다.
접근자 프로퍼티는 value 속성이나 writable 속성을 갖지 않는데, writable 속성은 setter 메서드의 존재 여부에 따라 결정되기 때문이다. 따라서 접근자 프로퍼티의 네 가지 속성은 get, set, enumerable, configurable이다.
프로퍼티의 속성 값을 질의하고, 값을 설정하는 ECMAScript 5의 메서드는 접근자 프로퍼티의 네 가지 속성을 표현하기 위해 프로퍼티 디스크립터라는 객체를 사용한다. 프로퍼티 디스크립터 객체는 임의의 프로퍼티가 가진 속성과 그 값들을 하나의 객체로 관리한다.
Object.getOwnPropertyDescriptor({x:1}, "x");
//[value:1, writable:true, enumerable:true, configurable:true]
Object.getOwnPropertyDescriptor({},"x"); //프로퍼티가 없으므로 undefined
Object.getOwnPropertyDescriptor({},"toString); //상속받은 프로퍼티이므로 undefined
프로퍼티의 속성을 설정하거나 임의의 속성으로 새 프로퍼티를 만들기 위해서는 Object.defineProperty()를 호출한다. 이때, 함수의 인자로, 수정할 객체와 추가하거나 변경할 프로퍼티 이름, 프로퍼티의 디스크립터 객체를 넘긴다.
var o={};
Object.defineProperty(o,"x,{value:1,writable:true,enumerable:false,configurable:true});
만약 동시에 여러 개의 프로퍼티를 만들거나 수정하고 싶을 때는 Object.defineProperties()를 사용
첫번째 인자는 수정하려는 객체, 두번째 인자는 객체에 만들거나 수정하려는 프로퍼티 이름과 프로퍼티 디스크립터 객체를 값으로 하는 객체
var p=Object.defineProperties({},{
x: { value: 1, writable: true, enumerable:true, configurable:true },
y: { value: 1, writable: true, enumerable:true, configurable:true },
r: {
get: function() { return Math.sqrt(this.x*this.x+this.y*this.y} },
enumerable:true,
configurable:true
}
});
Object.create의 두번째 선택 인자는 Object.defineProperties()의 두번째 인자와 같다.
Object.definePropertiy(ies)() 호출시 다음의 규칙을 위반하면 TypeError 예외가 발생한다.
객체 속성
모든 객체는 prototype, class, extensible 속성을 가지고 있다.
prototype 속성
prototype 속성은 객체가 만들어지는 시점에 설정된다. 객체 리터럴을 통해 만든 객체는 Object.prototype을 객체의 프로토타입으로 설정하고 new를 사용해 만든 객체는 프로토타입으로 사용할 생성자 함수의 protootype 프로퍼티 값을 사용하고, Object.create() 메서드로 만든 객체는 메서드의 첫 번째 인자로 넘긴 함수를 객체의 프로토타입으로 사용한다.
Object.getPrototypeOf()에 객체를 전달해 객체의 프로토타입을 검사할 수 있다.
var p={x:1};
var o=Object.create(p);
p.isPrototypeOf(o); //true o는 p를 상속받는다.
Object.prototype.isPrototypeOf(p); //true
class 속성
객체의 class 속성은 객체의 타입에 대한 정보를 담고 있는 문자열이다. 객체의 클래스의 정보를 알아볼려면 toString()메서드를 호출하고 반환되는 스트링의 아홉번째 문자부터 문자열 끝에서 두번째 문자까지 추출한다.
classof(new Date()) //Date
classof(window) //Window
classof(1) //Number
extension 속성
객체의 extension 속성은 객체에 새 프로퍼티를 추가할지 여부를 결정한다.
설명추가
객체 직렬화하기
객체 직렬화는 객체의 상태를 문자열로 변환하는 과정을 말한다.
JSON : Javascript Object Notationn
JSON.stringfy() : 객체를 직렬화
JSON.parse() : 직렬화한 문자열을 객체로 복원
JSON.parse() 함수는 문자열을 Date 객체로 복원하지 않는다.
Function, regexp, Error 객체와 undefined 값은 직렬화 할수 없기 때문에 복원할 수도 없다.
객체 메서드
toString() 메서드
어떠한 인자도 받지 않고, 메서드를 호출한 객체의 값을 어떠한 방식으로든 문자열로 만들어서 결과를 반환한다.
toLocaleString() 메서드
이 메서드의 목적은 객체의 지역화된 문자열 표현을 반환하는 것이다.
toJSON() 메서드
Object.prototype에는 toJSON() 메서드가 정의되어 있지 않다. 하지만 JSON.stringfy() 메서드는 정의되어 있는데, 이 메서드는 직렬화하려고 하면 객체 내부에 정의된 toJSON() 메서드를 찾는다. 만약 직렬화하려는 객체에 toJSON() 메서드가 있으면, toJSON() 메서드가 호출되고 객체가 직렬화 된 값을 반환한다.
valueOf() 메서드
toString()과 매우 유사하다. 이 메서드는 자스가 객체를 숫자와 같은 다른 원시 타입으로 변환하려 할 때 호출된다.
'책 > 자바스크립트 완벽 가이드' 카테고리의 다른 글
8장 함수 (0) | 2020.11.09 |
---|---|
7장 배열 (0) | 2020.11.08 |
5장 구문 (0) | 2020.11.06 |
4장 표현식과 연산자 (0) | 2020.11.05 |
3장 타입, 값, 변수 (0) | 2020.11.03 |