본문 바로가기

복습/Javascript

[Javascript] 객체지향 자바스크립트 [객체 part 2]

# ES6 객체 리터럴


* 변수의 이름과 속성 키가 같은 경우 ES6에서 약식구문 사용 가능

/*
일반적인 방법

let a = 1;
let b = 2;
let val = {a: a, b: b};
console.log(val); // { a: 1, b: 2 }

*/

// ES6

let a = 1;
let b = 2;
let val = {a, b};
console.log(val); // { a: 1, b: 2 }

* function: 를 생략 가능

/*
일반적인 방법

var obj = {
num: 1,
get: function(){
console.log(this.num);
}
}
*/
// ES6

var obj = {
num: 1,
get(){
console.log(this.num);
}
}
obj.get(); // 1

* ES6에서 속성의 키를 계산 가능 ( 보일러플레이트와 반복코드 제거 )

let vehicle = "car"
function vehicleType(){
return "truck"
}
let car = { [vehicle + "_model"]: "bmw"}
let truck = { [vehicleType() + "_model"]: "Mercedez"}
console.log(car)
console.log(truck)


# 객체 속성과 어트리뷰트 ( 부족한점이 있기에 추후에 다시 공부)


* enumerable (bool)  : 해당 객체의 키가 열거 가능한지 알려줌 true 라면 열거 가능

* configurable (bool) : 해당 객체의 속성을 제거할수 있는지 알려줌 true 라면 제거 가능 (delete)

* writable (bool) : 할당 연산자를 통해 값을 바꿀 수 있다. true 라면 변경 가능


* Object.getOwnPropertyDescriptor(object, prop) : object객체의 설명될 prop 속성 설명

* Object.getOwnPropertyDescriptors(object) : object 객체의 설명될 prop 속성들을 모두 설명

* Object.defineProperty(object, prop, descriptor) : object 객체에 직접 새로운 속성을 정의하거나 이미 존재하는 객체를 수정한뒤 그 객체 반환


/*
Object.defineProperty() 메서드에서 새로운 속성을 부여하면
enumerable, writable, configurable 의 디폴트값은 false
*/

var obj = {} // 객체 생성

Object.defineProperty(obj, 'a', {value: 1}); // 속성'a'의 value를 1로 설정
console.log(obj); // {} 출력 그 이유는 무엇일까?
console.log(Object.getOwnPropertyDescriptors(obj));
/*
출력
{ a:
{ value: 1,
writable: false, // 할당연산 불가능
enumerable: false, // 열거 불가능
configurable: false } } // 제거 불가능

열거가 불가능하기에 출력되지 않는다 또한 이미 configurable이 false이기때문에
속성 수정이 불가하다
*/
delete obj.a; // a를 delete한다 될까?
console.log(obj.a); // 1출력 , a속성은 configurable이 false이기때문에 delete 할 수 없다

Object.defineProperty(obj, 'b', {value: 2, configurable: true, enumerable: true, writable: false});
console.log(obj); // { b: 2 } b속성 추가
console.log(obj.a); // 1 열거되지않았을뿐이지 값은 살아있다
/*
obj의 b속성은 열거 가능하며 속성 수정도 가능하다 하지만 할당연산은 불가능하다
*/
obj.b = 55; // b의 값을 55로 변경
console.log(obj.b); // 2출력, b의 속성은 writable이 false 이기때문에 할당연산이 되지않는다

Object.defineProperty(obj, 'b', {enumerable: false}); // 열거를 불가능하게 했다
console.log(obj); // {} 출력, 열거가 불가능하다




# 디스트럭처링 (Destructuring)


* 배열의 요소나 객체의 속성을 배열리터럴, 객체리터럴 보다 간결하고 명확하게 변수에 할당할 수 있다

// 일반적인 코드
var config = {
server: "localhost",
port: "8080"
}
var server = config.server;
var port = config.port;

// 디스트럭쳐링이 훨씬 간결하다
var config = {
server: "localhost",
port: "8080"
}
let {server, port} = config;
console.log(server); // "localhost"
console.log(port); // "8080"



* 특정 속성을 선택해서 할당할 수 있다

// 디스트럭쳐링
var config = {
server: "localhost",
port: "8080",
timeout: 800
}
let {timeout: tt} = config; // config객체의 timeout속성을 지역변수 tt에 할
console.log(tt); // 800



* 이미 선언된 변수에 값을 할당할 수 있다

// 디스트럭쳐링
var config = {
server: "localhost",
port: "8080",
timeout: 800
}
let server = "192.xx.xx.xx";
let port = "90";

({server, port} = config)
/*
괄호를 묶어주는 이유는 자바스크립트 엔진이 {로 시작하는 블럭구문을 읽기때문에
괄호로 한번 묶어주는것입니다
*/
console.log(server) // "localhost"
console.log(port) // "8080"



* 존재하지 않는 속성이름을 가진 지역변수를 지정하면, undefined 값을 가짐

// 디스트럭쳐링
var config = {
server: "localhost",
port: "8080",
}
let {server, port, timeout} = config; // config에 timeout이 존재하지않음
console.log(timeout); // "undefined"



* 디폴트 값을 지정해서 undefined  값이 할당되지 않게 가능

// 디스트럭쳐링
var config = {
server: "localhost",
port: "8080",
}
let {server, port, timeout = 0} = config; // config에 timeout이 존재하지않지만 디폴트값을 지정해줌
console.log(timeout); // "0"



* 디스트럭처링은 배열에서도 가능하다, 구문만 바꾸면 된다, 객체 리터럴구문 -> 배열 리터럴구문

let array = [1, 2, 3];

/*
기존 구문

var one = array[0];
var two = array[1];
var three = array[2];

*/
// 디스트럭처링
var [one, two, three] = array;
console.log(one, two, three) // 1 2 3



* 일부요소를 건너뛸 수 있습니다

const days = ['Thursday', 'Friday', 'Saturday', 'Sunday']
const [,,sat, sun] = days; // days의 0 번째 1번째를 건너 뛰고 sat, sun에 할당되었다
console.log(sat, sun); // "Saturday" "Sunday"

let [first, ...rest] = [1, 2, 3, 4, 5, 6] // 나머지연산자를 통해 rest 배열에 2, 3, 4, 5, 6 요소 삽입
console.log(rest); // [ 2, 3, 4, 5, 6]



* 두 변수의 값을 교환할 때 임시변수 (temp)가 필요없습니다

let a = 1, b = 2;
console.log(a, b); // 1 2
/*

기존에 값을 변경하려면

let temp;

temp = a;
a = b;
b = temp;

이런방식으로 교환을 해야했다

*/
// 디스트럭쳐링을 통해 간단해진 방법
[b, a] = [a, b]; // 값 교환
console.log(a, b); // 2 1




# 내장 객체


* 데이터 랩퍼 객체 : Object, Array, Function, Boolean, Number, String 이 있으며 각각의 데이터유형해 해당한다. undefinednull 을 제외하고 모든 값에대한 데이터 래퍼 객체가 있다

* 유틸리티 객체 :  Math, Date, RegExp가 있으며 편리하게 사용가능

* 오류 객체 : Error 객체 뿐 아니라 동작 상태를 복구하는데 도움되는 여러 특정 객체들이 포함



* Object


모든 자바스크립트 객체들의 부모

모든 객체는 Object 를 상속

toString() 은 문자열을 반환하는 메서드

valueOf() 는 객체의 단일 값표현을 반환

아무리 복잡한 객체라도 모든 객체의 부모인 Object 객체를 상속받으므로 toString() 같은 메서드와 constructor 같은 속성을 제공


예) alert()

alert() 는 문자열에서만 작동을한다

alert() 함수를 호출 할 때 객체를 전달하면

toString() 메서드가 자동으로 호출됨


다음 그림은 결과과 둘다 동일



예) 문자열 연결


문자열 연결시에도 toString() 메서드가 먼저 호출됨

두개의 결과는 동일



* Array


Array() 는 배열을 생성할 때 사용 가능한 내장 함수

Array() 생성자를 통해 새로운 배열의 요소에 할당될 값을 전달 가능

단, 생성자에 숫자 하나를 전달하면 배열의 길이로 간주

배열은 객체다

배열이 특별한 이유는 

- 속성값(index)가 0부터 시작하는 숫자를 자동으로 지정

- 배열요소의 개수를 담는 length 속성

- 부모 객체에서 상속된 메서드보다 더 많은 내장 메서드 포함


length 속성을 배열의 현재 항목보다 크게 설정하면 공간이 마련됨

length 속성을 배열의 항목보다 작게 설정하면 뒤에 요소가 제거


다음은 이 위 항목에대한 '예' 이다

var arr1 = new Array();
var arr2 = [];

// 위의 두개 구문은 동일하다

var arr3 = new Array(1, 2, 3, "four"); // Array() 생성자에 할당할 값을 전달
console.log(arr3); // [1, 2, 3, 'four']

var arr4 = new Array(3); // 숫자 하나만 전달하는 예외경우
console.log(arr4); // [ < 3 empty items> ]
console.log(arr4.length); // 3
console.log(typeof arr4); // object 출력, 배열은 객체임을 증명

console.log(arr3.toString()); // 1,2,3,four 출력, toString()메서드를 가지고있다는것은 Object에서 상속받음
console.log(arr3.valueOf()); // [1, 2, 3, 'four']출력, 이유는 위와 동일
console.log(arr3.constructor); // [Function: Array]출력, 이유는 위와 동일

arr3.length = 6; // arr3의 길이는 원래 4이지만 6으로 늘림
console.log(arr3); // [1, 2, 3, 'four', <2 empty items>] 출력
console.log(arr3[5]); // "undefined" 존재하지 않는 공간에 접근시 undefined
console.log(arr3[10]); // "undefined" 존재하지 않는 공간에 접근시 undefined
console.log(arr3.length); // 6 출력 이전에 arr3[10]을 출력했지만 여전히 length는 6

arr3.length = 2; // arr3의 원래 길이보다 작게 설정
console.log(arr3); // [1, 2]출력, 뒤쪽이 제


Array 는 몇가지 유용한 배열 메서드를 가지고 있다

* sort() : 배열을 정렬하고 이를 반환

* join() : join() 에 전달된 문자열 매개변수를 함께 묶어 배열의 모든 요소 값을 포함하는 문자열 반환

* slice() : 소스배열을 수정하지 않고 배열의 일부분을 반환, 첫번째 매개변수는 begin, 두번째 매개변수는 end (begin은 포함 end는 미포함)

* push() : 배열의 끝에 새로운 요소 추가

* pop() : 배열의 마지막 요소를 반환 후 제거

* splice() : 기존 배열을 수정함, 첫번째 매개변수는 자르고싶은 begin, 두번째 매개변수는 자르고싶은 길이 end(begin부터 end까지 end미포함)

이후의 매개변수는 기존 배열의 제거된 자리에 채울 매개변수 전달(선택사항)


다음은 위 6가지 메서드의 예이다

let arr = [5, 6, 8, 12, 1, "helloArray"];

arr.sort(); // sort()를 통해 정렬
console.log(arr); // [ 1, 12, 5, 6, 8, 'helloArray' ]
/*
문제가 생겼다
그런데 1 다음 12가 온다
그 이유는 sort() 함수가
요소를 문자열로 변환하고 유니 코드 코드 포인트 순서로 문자열을 비교하여 정렬 하기 때문이다
그렇다면 sort() 함수 내에 비교하는 function을 넣어주는게 좋겠다
sort함수의 내부에
a와 b를 빼서
0 보다 작으면 -1 반환
0 이면 0 반환
0 보다 크면 1 반환인데

반환값이

-1이면 a를 먼저오게하고
0 이면 다른요소정렬
1 이면 a를 뒤로

*/
arr.sort(function(a, b){
return a - b;
})
console.log(arr); // [ 1, 5, 6, 8, 12, 'helloArray' ] 출력


arr.join('and');
console.log(arr); // [ 1, 5, 6, 8, 12, 'helloArray' ] join()메서드는 기존배열을 수정하지않습니다
let joinArr = arr.join('and '); // 'and '를 추가해 반환된 문자열을 tempArr에 할당
console.log(joinArr); //1and 5and 6and 8and 12and helloArray 출력


arr.slice(0, 2); // 0부터 2까지 자릅니다 2는 포함하지 않습니다
console.log(arr); // [ 1, 5, 6, 8, 12, 'helloArray' ] slice()메서드도 기존배열을 수정하지않습니다
let sliceArr = arr.slice(0, 2);
console.log(sliceArr); // [ 1, 5 ] 출력, 0부터 시작 2 미만 이므로 0번째, 1번째 요소만 자른 후 할당

console.log(arr.length); // 6 출력
arr.push("push"); // push를 배열요소에 삽입합니다
console.log(arr); // [ 1, 5, 6, 8, 12, 'helloArray', 'push' ] 출력
console.log(arr.length); // 7 출력 -> 요소가 추가됨

console.log(arr.pop()); // "push" 출력 맨 마지막 요소를 반환 후 제거
console.log(arr); // [ 1, 5, 6, 8, 12, 'helloArray' ] 출력


let spliceArr = arr.splice(1, 3, "new 1", "new 2");
// arr의 1번째배열부터 3개를 자른 값을 spliceArr에 할당하고 기존배열의 잘려진 자리에 "new 1""new 2"삽입
console.log(spliceArr); // [ 5, 6, 8 ]
console.log(arr); // [ 1, 'new 1', 'new 2', 12, 'helloArray' ]

* 추가적으로 lodash와 underscore 같은 라이브러리가 있다고 한다, 추후 공부

[참고]

 http://blog.jeonghwan.net/lodash/

http://harrythegreat.tistory.com/entry/%EC%96%B8%EB%8D%94%EC%8A%A4%EC%BD%94%EC%96%B4-%EC%A0%95%EB%A6%AC



# ES6 배열 메서드


* Array.from()


기존에는 배열과 유사한 값을 복사하는것이 문제였다. ES6에는 유용한 메서드를 도입했다 Array.from() 이다. 기존의 구문이다

// 기존의 구문
function toArray(){
var result = [];
for(var i = 0; i < arguments.length; i++){
result.push(arguments[i]);
}
return result;
}

function foo(){
var args = toArray(arguments);
console.log(args);
}
foo("hello", "javascript");

매우 복잡한데 Array.from() 하나만으로 해결됐다

// ES6 Array.from()
function foo(){
console.log(Array.from(arguments));
}
foo("hello", "javascript");

또한 Array.from() 을 호출 할 때 매핑 스키마를 제공할수 있다고한다. 지금은 그렇구나 하고 넘기려고 한다 ( 잘몰라서 ㅎ)

// ES6 Array.from() mapping schema
function foo(){
console.log(Array.from(arguments, function(elem)
{ return elem + "mapped"; }));
}
foo("hello", "javascript"); //[ 'hellomapped', 'javascriptmapped' ]


* Array.of()


Array() 생성자를 통해 배열을 만들게 되면 문제가 있다. 단일 숫자 하나를 전달하면 인수가 length 값으로 배열의 요소가 정해지지않은 배열이 생성된다

var array = new Array(3);
console.log(array); //[ <3 empty items> ]

ES6의 Array.of() 는 숫자의 유형에 상관없이 인수로 배열을 생성한다.

var array = Array.of("3");
console.log(array); // [ '3' ]

var array2 = Array.of(3);
console.log(array2); // [ 3 ]


Array.prototype


* Array.prototype.entires() : 배열의 각 index에 대한 key / value 쌍을 가지는 객체를 반환 (IE No Support)

* Array.prototype.values() : 현재 호환성 문제로 인해 제거되었습니다. 

참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/values


* Array.prototype.keys() : 배열의 각 key들을 갖는 새로운 객체를 반환  (IE No Support)


위의 두가지 메서드는 모두 iterator 를 반환하고 for 루프에서 사용 가능하다


let array = ['a', 'b', 'c']
let entiresArr = [];
let keysArr = [];
for(const [index, value] of array.entries()){
entiresArr[index] = [index, value];
}
console.log(entiresArr);
//[ [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ] 출력, key와 value를 entiresArr 배열에 저장

for(const key of array.keys()){
keysArr[key] = key;
}
console.log(keysArr); // [ 0, 1, 2 ]출력, key들을 keysArr에 저장

// 사실 스프레드연산자를 통해 for..of문 없이 가능하다
// 스프레드연산자는 배열을 받아 요소를 개별 변수로 분리한다

let newEntiresArr = [...array.entries()];
let newKeysArr = [...array.keys()];
// 훨씬 간결하고 명확하다

console.log(newEntiresArr) // [ [ 0, 'a' ], [ 1, 'b' ], [ 2, 'c' ] ]
console.log(newKeysArr) // [ 0, 1, 2 ]
// 결과는 동일하다
// 하지만 Array.prototype.keys() 는 빈 요소들의 key도 포함시킨다

let emptyArr = [1, 2, , 4, , 6]; // length가 6인 배열
let tempKeyArr = [...emptyArr.keys()];
console.log(tempKeyArr); // [ 0, 1, 2, 3, 4, 5 ] 출력, 빈 요소들도 key로 저장된다

* Array.prototype.find()  (IE No Support)

콜백함수를 실행해 그 결과가 참인 첫번째 요소를 반환, 콜백함수의 매개변수를통해 요소, 인덱스, 순회할 배열을 전달받는다

콜백함수의 조건에 만족하는 값을 반환 없으면 undefined return


* Array.prototype.findIndex() (IE No Support)

콜백함수를 실행해 그 결과가 참인 첫번째 요소의 인덱스값 반환, 없으면 -1 return

let numbers = [1, 2, 3, 4, 5, 6]
console.log(numbers.find(n => n > 5)); // 6 출력, 해당요소의 값
console.log(numbers.find(n => n > 6)); // undefined 찾지못했으므로 undefined

console.log(numbers.findIndex(n => n > 5)) // 5 출력, 해당요소의 index
console.log(numbers.findIndex(n => n > 6)) // -1 출력, 찾지못했으므로 -1

Array.prototype 이 어려운점이 많아서 추후에 다시 보기로