본문 바로가기
매일코딩/자바스크립트 개념

[자바스크립트 객체지향] 3 객체와 프로퍼티 종류 정리

by 인생여희 2017. 6. 22.
반응형

객체의 이해


자바스크립트 객체는 프로퍼티가 키/값 쌍으로 되어 있는 만큼 해시 맵에 빗대어 생각하면 이해하기 쉽다. 객체 프로퍼티에 접근할 때는 점 표기법 또는 각괄호 표기법 중 무엇을 해도 상관없다. 프로퍼티에 값을 할당하면 언제든 객체에 새 프로퍼티를 추가 할 수 있으며 delete 연산자를 사용하면 언제든 프로퍼티를 제거할 수 있다. 프로퍼티의 존재 여부는 프로퍼티 이름과 객체를 in 연산자와 함께 사용하면 알 수 있다. 이때 고유 프로퍼티만 확인하고 싶다면 모든 객체에 대 포함되어 있는 hasOwnProperty()를 사용하면 된다. 모든 객체 프로퍼티는 기본적으로 열거 가능하다. 열거 가능하다는 말은 for-in 반복문이나 Object.keys()를 사용할 때 볼 수 있다는 뜻이다.


프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 나누어진다. 데이터 프로퍼티는 값을 담아두기 위한 공간이므로 이 프로퍼티에서 값을 읽거나 저장할 수 있다. 데이터 프로퍼티에 함수를 담아두면 이 프로퍼티는 객체의 메소드로 취급된다. 데이터 프로퍼티와 달리 접근자 프로퍼티 자체에는 데이터를 저장할 수 없다. 접근자 프로퍼티는 게터와 세터를 조합하여 특정 동작을 수행한다. 데이터 프로퍼티와 접근자 프로퍼티 모두 객체 리터럴 표기법을 사용해 만들 수 있다.

 

모든 프로퍼티에는 관련된 내부 속성이 몇 가지 있으며 이러한 속성이 프로퍼티가 어떻게 동작하는지 결정한다. 데이터 프로퍼티와 접근자 프로퍼티에는 둘다 Enumerable Configurable 이라는 속성이 있다. 데이터 프로퍼티에는 WritableValue라는 속성이 추가로 있는 반면, 접근자 프로퍼티에는 GetSet 속성이 더 있다. 모든 프로퍼티의 EnumerableConfigurable 속성은 true가 기본 값이며 데이터 프로퍼티의 Writable 속성 역시 true가 기본 값이다. Object.defineProperty() 또는 Object.defindeProperties()를 사용하면 속성을 변경할 수 있으며 Object.getOwnPropertyDescriptor()를 사용하면 설정된 속성 값을 가져올 수 있다.

 

객체의 프로퍼티를 수정할 수 없게 만드는 방법은 세 가지가 있다. Object.preventExtensions()를 사용하면 객체에 프로퍼티를 추가할 수 없다. object.seal() 메소드를 사용하면 봉인된 객체를 만들 수 있는데 봉인된 객체는 확장 불가능해지며 객체의 프로퍼티는 설정 불가능해진다. Object.freeze()를 사용해 객체를 동결하면 객체가 봉인되는 것은 물론 데이터 프로퍼티에 값을 저장할 수도 없게 된다. 확장 불가능한 객체를 다룰 때는 잘못된 방식으로 객체에 접근했을 때 에러가 발생할 수 있도록 엄격한 모드를 사용하는 것이 좋다.

 

 

 자바스크립트의 객체는 동적이기 때문에 언제라도 바꿀 수 있다. 클래스 기반 언어는 클래스 정의에 따라 객체를 수정할 수 없도록 만들지만 자바스크립트 객체에는 이런 제약이 없다.

 

프로퍼티 정의

 

객체를 만드는 방법

1. Object 생성자를 사용하는 방법

2. 객체 리터럴 사용하는 방법

 

 

var parson 1 = { name :"kim"}

 

var person2 = new Object();

person2.name = "kim";

 

person1.age = 23

person2.age = 23

 

person1.name = "hong";

person2.name = "sim";

 

작성한 객체는 일부러 막아두지 않는 이상 언제든지 수정할 수 있다.

자바스크립트는 프로퍼티를 처음 객체에 추가할 때 객체에 있는 put 이라는 내부 메소드를 호출 한다. put 메소드는 객체에 프로퍼티 저장 공간을 생성한다. 이 동작은 수행하면 초기값은 물론 프로퍼티의 속성도 설정한다. 따라서 위의 예제에서 nameage 프로퍼티가 처음 정의 될 때마다 put 메소드가 호출된다.

 

put을 호출하면 객체에 고유프로퍼티가 만들어진다. 고유프로퍼티는 객체의 특정 인스턴스에 속해 있으며 인스턴스에 바로 저장된다.

 

기존 프로퍼티에 값을 할당 할때는 set이 호출 된다. set은 프로퍼티의 현재 값을 새 값으로 교체 한다.

 

프로퍼티 확인하기

 

잘못된 방법

 

if(person1.age){

 

}


 ****

이 방식의 문제점은 자바스크립트 타입 강제변환이 결과에 영향을 끼친다는 것이다. if 조건문은 주어진 값이 참스러운 값 (객체, 비어있지 않은 문자열, 0이 아닌 숫자, true) 이면 true로 취급하고 거짓스러운 값(null,undefined, false, NaN, 빈문자열) 이면 false로 취급한다. 객체 프로 퍼티에 거짓스러운 값이 저장될 수 있기 때문에 예제 코드와 같은 방식은 잘못 실행될 수 있다.

 

in 연산자 사용

in연산자는 특정 객체에 주어진 이름의 프로퍼티가 존재하는지 확인하고 존재하면 true를 반환한다. 사실 in연산자는 해시테이블에 주어진 키가 있는지 확인한다.

 

)

console.log("name" in person1) //true

consloe.log("age" in person1) //true

consloe.log("hobby" in person1) //false

 

 

자바스크립트에서 메소드는 함수를 참조하고 있는 프로퍼티이므로 메소드의 존재여부도 같은 방식으로 확인할 수 있다.

 

var person1 = {

name: "kim",

sayName: function(){

console.log(this.name)

}

 

}

 

console.log("sayName" in person1); // true

 


때로는 프로퍼티의 존재 여부를 확인하는 것에 그치지 않고 해당프로퍼티가 객체의 고유 프로퍼티인지도 확인해야 한다. in 연산자는 고유프로퍼티와 프로토타입 프로퍼티 둘다 찾기 때문에 고유 프로퍼티인지 확인하고 싶다면 다른 방법을 동원해야 한다. 모든 객체에 포함되어 있는 hasOwnProperty() 메소드는 주어진 프로퍼티가 객체에 존재하는 동시에 고유 프로퍼티일 때만 true를 반환한다.

 

var person1 = {

name: "kim",

sayName: function(){

console.log(this.name)

}

 

}

 

console.log("name" in person1); // true

console.log("person1.hasOwnProperty("name")); // true

 

console.log("toString" in person1); // true

console.log("person1.hasOwnProperty("toString")); // false

 

nameperson1의 고유 프로퍼티이므로 in 연산자와 hasOwnProperty()는 둘다 true를 반환한다. 하지만 toString() 메소드는 모든 객체에 포함되어 있는 프로토타입 프로퍼티이므로 in 연산자는 true를 반환하지만 hasOwnProperty()false를 반환한다.

 

프로퍼티 제거

프로퍼티 값을 null로 바꾸는 것만으로 프로퍼티가 객체에서 환전히 제거 되지 않는다. 값을 null로 바꾸는 동작은 set 내부 메소드를 null과 함께 실행하므로 앞에서 보았듯 프로퍼티의 값만 달라지게 된다.

객체에서 프로퍼티를 완전히 제거할 때는 delete 연산자를 사용해야 한다.

 

객체프로퍼티에서 delete 연산자를 사용하면 내부적으로 delete가 호출된다. 무사히 실행을 마쳤을 때 true를 반환한다.

 

 

var person1 = {

name: "kim",

sayName: function(){

console.log(this.name)

}

 

}

 

console.log("name" in person1) //true

 

delete person1.name;

console.log("name" in person1); //false

console.log(person1.name) //undefined

 

존재하지 않는 프로퍼티에 접근하면 undefinde가 반환된다

 

 

열거

객체에 추가하는 프로퍼티는 기본적으로 열거가 가능하다.

for- in 문을 사용해서 훑을 수 있다. 열거 가능 프로퍼티에는 Enumerable 이라는 내부속성이 true로 설정되어 있다.

 

var property;

for(property in object){

console.log("이름" + property);

console.log("" + object[property]);

}

 

 

객체 프로퍼티의 목록을 가져와야 한다면 ECMAScript 5에서 도입된 Object.keys()메소드가 유용하다.

 

var properties = Object.keys(object);

var I, len;

 

for(i=0, len=properties.length; i<len; i++){

console.log("name" + properties[i]);

console.log("value" + object[properties[i]]);

 

}

 

일반적으로 프로퍼티 이름 목록을 배열 형태로 다루고 싶을때는 Object.keys()를 사용하고 굳이 배열이 필요 없을 때는 for-in을 사용한다. for-in 반복문에서는 열거 가능한 프로토타입 프로퍼티도 반환하지만, object.keys()는 고유 프로퍼티만 반환한다.

 

모든 프로퍼티를 열거할 수 있는 것은 아니다. 객체의 네이티브 메소드는 대부분 Enumerable 속성이 false로 되어 있다. 특정 프로퍼티가 열거 가능한지 확인할 때는 propertyIsEnumerable() 메소드를 사용하면 된다. 이 메소드는 모든 객체에 포함되어 있다.

 

var person1 = {

name: "kim"

 

}

 

console.log("name" in person1) //true

console.log(person1.propertyIsEnumerable("name")); //true

 

var properties = Object.keys(person1);

 

console.log("length" in properties); // true

console.log(properties.propertyIsEnumerable("length")) //false

 

nameperson1에 직접 추가한 프로퍼티이므로 열거 가능하다. 반면 properties 배열의 length 프로퍼티는 Array.prototype의 내장 프로퍼티이므로 열거 가능하지 않다. 네이티브 프로퍼티는 대부분 기본적으로 열거 가능하지 않다.

 

프로퍼티 종류

1.데이터 프로퍼티 : 값을 포함하고 있다. put 메소드의 기본 동작을 통해 생성 된다.

2.접근자 프로퍼티: 값을 포함하지 않는 대신 프로퍼티를 읽었을 때 호출할 함수 게터(getter)와 값을 설정할 때 호추할 함수 세터(setter)를 정의 한다.


)

var person1 = {

name: "kim",

 

get name(){

console.log("name 읽는 중“);

return this.name;

},

 

set name(value){

 

console.log("name의 값을 %s로 설정하는 중", value);

this.name = value;

}

 

};

console.log(person1.name); //name 읽는 중 kim 출력

 

person1.name = "hong";

console.log(person1.name) //name의 값을 hong으로 설정하는 중 출력후 hong

 

이 예제는 name이라는 접근자 프로퍼티를 정의하고 있다. 접근자 프로퍼티에서 사용할 실제 값은 _name이라는 데이터 프로퍼티에 저장한다. name의 게터와 세터를 정의할 때는 function 키워드가 없다는 점만 제외하면 함수를 정의하는 문법과 비슷하다. get 또는 set이라는 특수한 키워드를 접근자 프로퍼티 이름앞에 사용하고 이름 뒤에는 괄호와 함수 코드를 입력한다. 게터는 값을 반환해야 하고 세터는 프로퍼티에 할당할 값을 인수로 전달 받을 수 있어야 한다.

 

프로퍼티 속성


공통속성

데이터 프로퍼티와 접근자 프로퍼티의 공통속성은 두가지다.

하나는 Enumerable 인데 프로퍼티가 열거 가능한지 정하는 속성이고 다른 하나는 Configurable로서 프로퍼티를 변경할 수 있는지 정하는 속성이다. Configurable 속성이 있는 설정가능 프로퍼티는 delete 연산자를 사용해 언제든 제거할 수 있고 속성도 언제든 변경할 수 있다. 다시 말해 설정 가능 프로퍼티는 데이터 프로퍼티를 접근자 프로퍼티로 바꾸거나 그 반대로 바꾸는 것도 가능하다. 객체의 모든 프로퍼티는 따로 설정을 하지 않는 한 열거 가능하며 설정 가능하다.

 

객체의 속성을 바꾸고 싶을 때는 object.defineProperty()메소드를 사용할 수 있다. Object.defineProperty() 메소드에는 인수를 세 개 전달하는 데 첫 번재 인수는 프로퍼티를 소유하고 있는 객체 이고 두 번째 인수는 프로퍼티 이름, 세 번째 인수는 설정할 프로퍼티 속성 값을 포함하고 있는 프로퍼티 서술자 객체이다.

 

var person1 = {name:"kim"}

//열거 불가능

Object.defineProperty(person1,"name",{

enumerable:false});

 

console.log("name" in person1); //true

console.log(person1.propertyIsEnumerable("name")); //false

 

var properties = Object.keys(person1);

console.log(properties.length) // 0

 

//설정불가능

Object.defineProperty(person1,"name",{

configurable:false

});

 

//

delete person1.name;

console.log("name" in person1); //true

console.log(person1.name); //kim

 

//에러

Object.defineProperty(person1,"name",{

configurable:true

});

 

 

데이터 프로퍼티 속성

1. value:

2. writable: 프로퍼티에 값을 저장할 수 있는지 정의하는 불리언 값

 

var person1 ={name : "kim"}


두개는 같다


var person1 = {};

Object.defineProperty(person1,"name",{

value: "kim",

enumerable:true,

configurable:true,

writable:true

 

});

 

Object.defineProperty()를 실행하면 먼저 해당 프로퍼티가 있는지 확인한다. 프로퍼티가 없다면 새 프로퍼티를 추가하고 프로퍼티 서술 객체에 정의한 대로 속성을 설정한다. 이 예제에서 nameperson1의 프로퍼티가 아니었기 때문에 새로 작성된다.

Object.defineProperty()를 사용해 새 프로퍼티를 정의할 때 서술자에 없는 속성은 모두 false로 설정된다. 따라서 필요한 속성은 반드시 서술자에 포함시켜 두어야 한다.


다음 예제는 Object.defineProperty()를 호출할 때 명시적으로 true라고 설정한 속성이 없었기 때문에 열거 불가능, 설정불가능, 쓰기 불가능한 name 프로퍼티를 만든다.

 

 

var person1 = {};

 

Object.defineProperty(person1,"name",{

value: "kim"

});

 

console.log("name" in person1); //true

console.log(person1.propertyIsEnumerable("name")); //false

 

delete person1.name;

console.log("name" in person1); //true

 

person1.name = "hong";

console.log(person1.name); //kim

 

접근자 프로퍼티 속성


접근자 프로퍼티는 저장할 값이 필요 없으므로 valuewritable 속성은 필요 없다

대신 setget 속성이 필요하다.

 

접근자 프로퍼티를 정의할 때 객체 리터럴 형식 대신 접근자 프로퍼티 속성을 사용하면 기존에 있던 객체에도 프로퍼티를 추가할 수 있다는 장점이 있다.

 

var person 1 ={_name:"kim",

 

get name(){

console.log("name 읽는 중")

return this._name;

},

set name(value){

console.log("name의 값을 %s로 설정하는중",value);

this._name=value;

}

 

}

 

이 코드는 다음과 같이 작성할 수 있다.

 

var person1= {_name:"kim"};

 

Object.defineProperty(person1,"name",{

get:function(){

console.log("name 읽는 중")

return this._name;

},

set:function(value){

console.log("name의 값을 %s로 설정하는중",value);

this._name=value;

 

},

enumerable:true,

configurable:true

});

 

Object.defineProperty()에 전달된 객체에 있는 get키와 set키는 값이 함수인 데이터 프로퍼티이다. 이 객체에서는 객체 리터럴 형태의 접근자를 사용할 수 없다.

접근자 프로퍼티에도 enumerable 이나 configurable 과 같은 속성을 설정하여 프로퍼티의 작동 방식을 조정할 수 있다. 설정안하면 false 가 설정된다.

 

여러프로퍼티 정의하기

Object.defineProperty() 대신 Object.defineProperties()를 사용하면 동시에 여러 프로퍼티를 설정할 수 있다. 이 메소드에는 인수를 두 개 전달한다. 첫 번째 인수는 대상 객체이고, 두 번째 인수는 정의할 프로퍼티의 정보를 담고 있는 객체이다.

 

var person1 = {};


Object.defineProperties(person1,{

 

//데이터를 정의할 데이터 프로퍼티

_name : {

value: "kim",

enumerable:true,

configurable:true,

writable:true

},

//접근자 프로퍼티

 

name :{

get:function(){

console.log("name 읽는 중");

return this._name;

},

set:function(value){

console.log("name의 값을 %s로 설정하는중",value);

this._name=value;

},

 

enumerable:true,

configurable:true

 

}

 

})

 

프로퍼티 속성 가져오기

Object.getOwnPropertyDescriptor() 메소드 사용

고유프로퍼티만 사용가능

첫 번째 인수: 대상객체

두 번째 인수: 정보를 가져올 프로퍼티의 이름

인수로 이름을 전달한 프로퍼티가 존재하면 프로퍼티의 속성 정보를 포함한 객체가 반환되며, 이 객체에는 configurable, enumerable을 비롯한 네 종류의 키가 설정되어 있다.

 

var person1 = {

name:"kim"

};

 

var descriptor = Object.getOwnPropertyDescriptor(person1,"name");

 

console.log(descriptor.enumerable); //true

console.log(descriptor.configurable); //true

console.log(descriptor.writable); //true

console.log(descriptor.vlaue); //kim

 

 도서: 객체지향 자바스크립트의 원리 정리

 

반응형

댓글