Object 오브젝트 -ECMAScript
ES6
에서 Object
오브젝트에 작성하고 제어하는 방법이 추가 되었습니다.
어떤것은 ES5와 다르게 변경된 것도 있습니다.
Object
오퍼레이션
Object에 같은 key 사용
(var obj= {key: value}) 형태에서 key 값이 같은 프로퍼티를 두 개 작성했을 때
자바스크립트 에디션(버젼)별로 차이가 있습니다.
ES3에서는 key 값이 같더라도 추가되고 ES5의 strict 모드에서는 에러가 발생합니다.
ES6에서는 strict 모드에 관계없이 에러가 발생하지 않으며 나중에 작성한 프로퍼티 값으로 대체됩니다.
1 | ; |
오브젝트 프로퍼티 키 값이 one인 프로퍼티 두 개를 작성 했습니다.ES6 버젼에서 첫 번째의 one 프로퍼티 값 1이 두 번째 프로퍼티 값 2로 대체됩니다.
나중에 작성된 프로퍼티는 값만 대체되고 추가되지 않습니다.
{one:2}가 두 개 작성되지 않습니다.
변수 이름으로 값 설정
변수 이름을 사용하여 Object
의 프로퍼티 값을 설정할 수 있습니다.
1 | let one = 1, two = 2; |
(let values = {one, two})에서 one이 프로퍼티 이름이 되면서 one 변수 값인 1이
프로퍼티 값으로 설정됩니다.
two 역시 프로퍼티 이름이 되면서 변수 값인 2가 프로퍼티 값으로 설정됩니다.
{one, two} 의 형태가 변수의 이름을 사용하여 프로퍼티 이름이 되면서
변수의 값이 프로퍼티 키로 할당되어 {one:1, two:2}형태로 변환 됩니다.
Object에 function 작성
ES5
에서는Object
에 함수를 아래와 같은 형태로 작성합니다.
1 | let obj = { |
ES6
에서Object
에 함수(메서드)를 다른 방법으로 작성할 수 있습니다.
1 | let obj = { |
getTotal(param) {}
형태와 같이 클론(;
)과 function
키워드를 작성 하지 않습니다.
이 형태의 설명은 다음에 다루고 여기서는 바뀐 형태와 그에 따른 코드 작성의 편리함 정도만 알고 넘어갑니다.
디스크립터
디스크립터(Descriptor
)는 ES5에서 제시되었으며 이를 바탕으로ES6
에서 여러 기능들이 추가 되었습니다.
내용의 연결을 위해 간단하게 요점만 다룹니다.
1 | Object.defineProperty({}, "book", { |
“book”은 프로퍼티 이름입니다. 프로퍼티 이름 이외에
{value: 123, enumerable: true}가 프로퍼티 디스크립터입니다.
프로퍼티 디스크립터는 속성 이름(enumerable
)과 속성 값(true
)으로 구성됩니다.
프로퍼티 디스크립터는 데이터 프로퍼티 디스크립터 타입과 엑세스(access
) 디스크립터 타입으로 분류됩니다.
프로퍼티 디스크립터
타입 | 속성 이름 | 속성 값 형태 | 디폴트 값 | 개요 |
---|---|---|---|---|
데이터 | value | Javascript 데이터 타입 | undefined | 프로퍼티 값으로 사용 |
writable | true, false | false | false: 속성 값 변경 불가 | |
엑세스 | get | function, undefined | undefined | 프로퍼티 getter 함수 |
set | function, undefined | undefined | 프로퍼티 setter 함수 | |
공용 | enumerable | true, false | false | false: for-in으로 열거 불가 |
configurable | true, false | false | false: 프로퍼티 삭제 불가 |
데이터 타입 속성과 엑세스 타입 속성을 같이 작성할 수 없습니다.
“{value:1, get: function(){}}” 형태과 같이 value 속성과 get 속성을 같이 작성하면 에러가 발생합니다.
스펙에서 {writable: true}
형태를 [[writable]]:true
로 기술하고 있습니다.enumerable
, configurable
도 같습니다.
위와 같이 대괄호 두 개 [[]] 사이에 속성 이름을 작성합니다.
get, set 속성
get
속성은 getter
기능을 제공하고 set
속성은 setter
기능을 제공합니다.
아래는 ES5 에서의 사용 형태 입니다.
ES5 get
1 | var obj = {}; |
- 엔진이 코드를 해석하면
obj
오브젝트에서book
프로퍼티 작성 여부를 체크합니다.book
프로퍼티가 작성되어 있으면get
속성의 존재 여부를 체크합니다.
- 존재한다면
get
속성 값인 함수를 실행합니다. 이것이 getter 입니다.
getter
가 호출되어 호출된 값“책”
이 반환됩니다.
getter는 obj.book()과 같이 함수를 호출하는 형태로 작성하지 않고
obj.book과 같이 함수 이름만 작성합니다.
ES5 set
1 | var obj = {}; |
obj
오브젝트에서item
프로퍼티 작성 여부를 체크합니다.
- 작성되어 있으면
set
속성의 존재 여부를 체크합니다. 존재 하면set
속성 값인 함수를 실행합니다.
- 이때 “야구” 값 을 실행하는 함수의 파라미터 값으로 넘겨줍니다.
이것이 setter 입니다.
setter
가 호출되면 this.sports
에서 this
가 obj
오브젝트를 참조합니다.
파라미터로 넘겨받은 “야구”
를 obj
오브젝트의 sports
프로퍼티에 할당합니다.
(obj.sports)
형태는 getter
입니다. 하지만 obj
오브젝트에 get
속성을 작성하지 않았습니다. 디폴트로 getter가
호출되어 obj
오브젝트의 sports
프로퍼티 값을 반환 합니다.
ES6 get
ES6에서는 보다 직관적으로 getter와 setter를 정의할 수 있습니다.
1 | let obj = { |
ES6에서 getter는 함수(메서드) 이름 앞에 명시적으로 “get”을 작성합니다.
- (
obj.getValue
)와 같이 함수 이름을 작성합니다.
getter getValue
가 함수로 호출 됩니다.
this
는obj.getValue
의obj
오브젝트를 참조 합니다.
obj
오브젝트에value
프로퍼티가 있으므로 값을 반환 합니다.
ES6 set
1 | let obj = { |
setter
또한 ES6
에서 함수(메서드) 이름 앞에 명시적으로 “set”
을 작성합니다.
- (
obj.setValue = 123
)과 같이obj
오브젝트의setValue
를 프로퍼티 키로 하여
값을 할당하는 형태로 작성합니다.
setValue
가setter
이므로 함수로 호출되고 123을 파라미터 값으로 넘겨줍니다.
this
는obj.setValue
의obj
오브젝트를 참조합니다.
is(): 값과 값 타입 비교
Object.is()
메서드는 두 개의 파라미터 값과 값 타입을 비교하여 같으면 true
, 다르면 false
를 반환 합니다.
값과 값 타입을 비교하는 것이지 오브젝트를 비교하는 것이 아닙니다.
배열[]과 배열[] 비교, 오브젝트{}와 오브젝트{} 비교는 false가 반환됩니다.
단, window
오브젝트를 비교하면 true
를 반환합니다.
값을 비교하는 방법마다 차이가 있습니다.
- ===
값과 값 타입을 모두 비교합니다.- ==
타입은 비교하지 않고 값만 비교합니다.- Object.is()
값과 값 타입을 모두 비교합니다.
Object.is() 와 === 의 차이
Object.is()와 ===는 값과 값 타입을 비교하는 점은 같습니다.
차이점.
- +0 과 -0을 비교하면
- Object.is()는 false
- ===는 true를 반환합니다.
- NaN 과 NaN을 비교하면
- Object.is()는 true를 반환하고
- ===는 false를 반환합니다.
1 | console.log("1:", Object.is(1, "1")); |
assign(): 오브젝트 프로퍼티 복사
Object.assign()
메소드는 열거할 수 있는 하나 이상의 출처 객체로부터 대상 객체로 속성을 복사할 때 사용합니다. 대상 객체를 반환합니다.
Object.assign(target, …sources)
- 형태 : Object.assign()
- target : 열거 가능한 오브젝트 지정
- sources : 열거 가능한 오브젝트, 다수 지정 가능, sources 지정안할 시 target 오브젝트 반환
두 번째 파라미터의 오브젝트에서 own 프로퍼티만 복사합니다.
prototype과 프로퍼티 디스크럽터는 복사하지 않습니다.
own 프로퍼티: 오브젝트 자체에서 작성한 프로퍼티를 나타내며 상속받은 프로퍼티는 포함되지 않습니다.
첫 번째 파라미터 null 값
1 | try { |
Object.assign()
의 첫 번째 파라미터를 지정하지 않거나null
또는undefined
로 지정하면TypeError
가 발생합니다.null
로 지정했으므로catch(e)
가 실행되어“null 지정 불가”
가 출력됩니다.
Object.assign()의 첫 번째 파라미터에 Number, Boolean, String, Symbol 값을 지정하면 값 타입의 오브젝트를 생성하고
파라미터 값을 생성한 오브젝트의
[[PrimitiveValue]]
에 설정합니다.
첫 번째 파라미터가 123Number
오브젝트 이므로Number
오브젝트를 생성하고[[PrimitiveValue]]
에 123을 설정합니다. 마지막으로 생성한 오브젝트를 반환 합니다.
- 첫 번째, 두 번째 파라미터 모두 열거 가능한 오브젝트가 아닙니다.
첫 번째 오브젝트는Number
오브젝트 이므로 2번과 같은 방식으로 설정되고 반환됩니다.
하지만 두 번째 오브젝트는 복사되지 않고 반환됩니다.
첫 번째 파라미터 String
1 | 1. console.log(Object.assign("ABC", {one: 1})); |
Object.assign()
첫 번째 파라미터가String
오브젝트 입니다.String
오브젝트를 생성하고[[PrimitiveValue]]
에“ABC”
가 설정되고String
오브젝트는 이터러블 오브젝트 이기 때문에{one:1}
을 복사하고 생성한 오브젝트를 반환합니다.
Object.assign()
첫 번째 파라미터가Symbol
이므로Symbol
오브젝트를 생성합니다.[[PrimitiveValue]]
에Symbol(“ABC”)
를 설정합니다.
생성된Symbol
오브젝트에{one:1}
을 복사합니다.
Object.assign()
의 파라미터가 모두 문자열이므로TypeError
가 발생합니다.
파라미터 값으로 undefined, null 작성시
1 | let oneObj = {}; |
Object.assign()
파라미터에undefined
,null
을 작성하면 복사되지 않습니다.
Object.assign()
파라미터에 오브젝트로 작성하고 오브젝트에 프로퍼티 값으로undefined
와null을
작성하면 복사됩니다.
undefined, null를 오브젝트 프로퍼티 값으로 작성시 복사 가능하지만, 파라미터에 값으로 작성하면 복사되지 않습니다.
assign() 필요성
일반적으로 Object
오브젝트를 변수에 할당하면 프로퍼티가 연동되어 한 쪽의 프로퍼티 값을 바꾸면 다른 한 쪽의 프로퍼티 값이 자동으로 바뀝니다.
1 | let sports = { |
sports
오브젝트를dup
변수에 할당하면dup
변수의 오브젝트와sports
오브젝트의 프로퍼티가 연동됩니다.
console.log(dup);
{event: “농구”, player: 55}
- event: “농구”
- player: 55
- __proto__: Object
즉, 한 쪽 오브젝트 프로퍼티 값을 바꾸면 다른 쪽의 프로퍼티 값이 자동으로 변경됩니다.
sports
오브젝트의player
프로퍼티에 55를 할당 합니다.dup
오브젝트의player
프로퍼티 값이 55로 변경됩니다.sports
오브젝트는 원본이고dup
오브젝트가 복사본입니다.
- 복사본
dup
오브젝트의event
프로퍼티에 값을 할당하면
원본sports
오브젝트의event
프로퍼티 값이 자동으로 변경됩니다.
값을 연동하여 사용하고 싶지 않은 경우
Object.assign()
으로 복사하여 사용하면 프로퍼티 값이 연동되지 않습니다.
1 | let sports = { |
Object.assign()
두 번째 프로퍼티에 지정한sports
오브젝트의 프로퍼티를
첫 번째 파라미터에 지정한 빈 오브젝트{}에 복사합니다.
그리고 첫 번째 파라미터의 오브젝트를 반환하여dup
변수에 할당합니다.
dup
오브젝트의player
프로퍼티에 33을 할당합니다.sports
오브젝트의player
프로퍼티 값은 연동되지 않습니다.
sports
오브젝트의event
프로퍼티에 “수영”을 할당합니다.dup
오브젝트의 프로퍼티 값은 연동되지 않습니다.
assign() 고려사항
Object.assign()
은 복사한 값이 연동되지 않아 좋지만 고려할 점도 있습니다.
1 | let oneObj = {one: 1}; |
twoObj
의 프로퍼티를oneObj
에 복사하고mergeObj
에 할당합니다.oneObj
와mergeObj
의 프로퍼티가 같으므로ture
가 출력됩니다.
Object.assign()
으로 복사한 첫 번째 파라미터(oneObj
)와 두 번째 파라미터(twoObj
)의 프로퍼티는 연동되지 않지만,
첫 번째 파라미터 오브젝트(oneObj)를 할당한 오브젝트(mergeObj)는 연동됩니다.
assign() getter
오브젝트의 프로퍼티를 복사할 때 프로퍼티가 getter
이면
함수를 복사하지 않고 함수를 호출하여 반환된 값을 복사합니다.
return
문을 작성하지 않으면 undefined
를 반환합니다.
1 | let count = { |
- 두 번째 파라미터
count
오브젝트에 작성된 순서로 복사합니다.count
오브젝트의current
프로퍼티를 복사 하고 그 값은 1입니다.
다음getCount()
함수를 복사하는 대신 함수를 호출하고 반환된 값을 복사합니다.
함수에서this.current
에 1을 더하므로 프로퍼티 값은 2가 되고 2를 반환합니다.
count
오브젝트의current
프로퍼티를 복사하는 시점의 값은 1입니다.
그 후에getCount()
함수를 호출하면서 1을 더하지만
원본 프로퍼티 값이 변경되더라도 복사된mergeObj
의 프로퍼티 값은 연동되지 않으므로current
값은 1입니다.getCount
는 함수가 호출되고 반환된 값인 2가mergeObj
의 복사되어 출력됩니다.
setPrototypeOf(): __proto__에 첨부
Object.setPrototypeOf()
메소드는 지정된 객체의 프로토타입 (즉, 내부 [[Prototype]] 프로퍼티)을 다른 객체 또는 null
로 설정합니다
첫 번째 파라미터의 __proto__에 두 번째 파라미터를 첨부합니다.
Object.setPrototypeOf(obj, prototype);
obj
오브젝트 또는 인스턴스 (프로토타입 설정을 가지는 오브젝트)
오브젝트에 프로퍼티를 추가 할 수 없는 오브젝트이면 TypeError 발생prototype
객체의 새로운 프로토 타입 (오브젝트 or null).
경고
오브젝트의 [Prototype]을 변경하는 것은 모든 브라우저와 JavaScript 엔진에서
단순히 obj.__proto__ = … 문에 소요 된 시간으로 제한되지 않고
변경된 [Prototype]에 접근할 수 있는 모든 코드로 확장될 수 있습니다.
성능에 신경을 쓰면 [[Prototype]] 설정을 피해야 합니다.
[[Prototype]]을 변경하는 대신 Object.create()를 사용하여 원하는 [[Prototype]]으로 새 오브젝트를 만듭니다.
1 | let Sports = function(){}; |
setPrototypeOf()
의 두 번째 파라미터인Sports.prototype
에 연결된 프로퍼티를 첫 번째 파라미터인 빈 오브젝트{}의 __proto__에 첨부합니다.
- protoObj: Sports
- __proto__: Object ①
- getCount: ƒ ()
- constructor: ƒ ()
- __proto__: Object
protoObj
에 __proto__가 연결되어 있으며 여기에Sports.prototype
의constructor
와getCount
가 연결되어 있습니다.
protoObj
의 __proto__에getCount
가 있으므로protoObj.getCount()
형태로 호출할 수 있습니다.
1 | let Sports = function(){}; |
- 예제1에서는
Object.setPrototypeOf()
의 두 번째 파라미터를Sports.prototype
를 지정 했습니다.
예제 2에서는Sports
함수를 지정합니다.Sports
함수를 첫 번째 파라미터 오브젝트의 __proto__에 첨부하여 반환합니다.
fnObj.getCount
를 실행하면Sports.prototype
에 연결된getCount
가 반환되지 않고undefined
가 반환됩니다. 이는fnObj
또는 fnObj.__proto__에getCount
가 없다는 의미입니다.setPrototypeOf()
두 번째 파라미터에Sports.prototype
이 아닌Sports
를 지정하면Sports.prototype
에 연결된 메서드를 직접 호출할 수 없습니다.
- 이와 같이 경로를 지정해 호출해줘야 합니다.
fnObj.__proto__.prototype.getCount()가 전체 경로이지만,
__proto__는 작성하지 않아도 되므로fnObj.prototype.getCount.call(Sports)
형태로 작성하면getCount()
를 호출할 수 있습니다.
__proto__
new
연산자로 생성된 인스턴스 또는
다른 오브젝트의prototype
에 연결된 프로퍼티가 __proto__에 첨부됩니다.
- __proto__는 엑세스 프로퍼티 입니다.
즉, getter와 setter 기능이 있습니다.
- __proto__는 [[Enumerable]]: false이고
[[Configurable]]: true 입니다.
- 인스턴스를 생성하면 오브젝트의
prototype
에 연결된 프로퍼티가
인스턴스의 __proto__에 첨부됩니다.
- 이 환경이 만들어지면
prototype
에 연결된 메서드를 인스턴스 메서드로 호출할 수 있습니다.
브라우저 개발자 도구에서 __proto__에 첨부되는 것처럼 표시되는 것은 개발자가 인식 하기 쉽도록 하기 위한 것으로 실제로는
원본 오브젝트의prototype
에 연결된 프로퍼티를 참조합니다.
이것을prototype 공유
(share)라고 합니다.첨부라는 말은 기능의 표면적 이해를 돕기 위한 것일뿐 실제로 prototype에 프로퍼티를 복사하는 것은 아닙니다. 참조 혹은 prototype 공유가 맞습니다.
prototype과 __proto__ 차이
__proto__에 있는 메서드는 object.name()
형태로 직접 호출할 수 있지만 prototype
에 연결된 메서드는 object.prototype.name.call()
형태로 호출해야 합니다.
1 | 1. let Sports = function(){}; |
Sports.prototype
에get
메서드를 연결하고new
연산자로 인스턴스를 생성하여sportsObj
에 할당합니다. sportsObj.__proto__에 get()메서드가 첨부됩니다.
- 생성한
sportsObj
인스턴스의 __proto__에set()
메서드를 추가합니다. 이때 __proto__에 추가하더라도 __proto__에 추가되지 않고Sports.prototype
에 추가됩니다.
왜냐하면set()
메서드를Sports
로 생성한 다른 인스턴스에서 공유하기 때문입니다.
set()
메서드가 호출되면 인스턴스의 __proto__에 있지만,
__proto__은 프로퍼티 검색과 경로 제공을 위한 것으로,
실제로 호출되는 메서드는 인스턴스를 생성한Sports.prototype
에 연결된 메서드입니다.
이것이prototype
의 프로퍼티 공유 개념이며 자바스크립트의 아키텍처입니다.
sportsObj
의 __proto__에 set()메서드를 추가했는데,Sports.prototype.set
으로 코드가 출력된 것은 실제로Sports.prototype
에 추가되기 때문입니다.