[포스코x코딩온] KDT-Web-8 2주차 회고2 - Javascript

Yunes·2023년 7월 12일
0

[포스코x코딩온]

목록 보기
5/47
post-thumbnail

📘 학습

자바스크립트는 Object, String, Number, Boolean, Symbol, Date, Math, RegExp, Array, Map/Set, WeakMap/WeakSet, Function, Promise, Reflect, Proxy, JSON, Error 등 40여개의 표준 빌트인 객체를 제공한다. Math, Reflect, JSON 을 제외한 표준 빌트인 객체는 모두 인스턴스를 생성할 수 있는 생성자 함수 객체이며 이들은 프로토타입 메서드와 정적 메서드를 제공하고 생성자 함수 객체가 아닌 표준 빌트인 객체는 정적 메서드만 제공한다.
---모던 자바스크립트 Deep Dive---

표준 빌트인 객체 : ECMAScript 사양에 정의된 객체이므로 자바스크립트 실행환경 (브라우저, Node.js 환경)과 관계없이 언제나 사용 가능하다.

금일 문자열 기반, 배열 기반 메서드와 Math, Date 와 관한 메서드들에 대해 배웠다. 그렇다면 우선 메서드가 무엇인지부터 확인해야겠다는 생각이 들었다.

이번 포스트는 다음과 같은 흐름으로 설명하려고 한다.

  1. 우선 자바스크립트가 객체 기반의 프로그래밍 언어이니 객체지향 프로그래밍이 무엇이고 그래서 메서드가 어떤 것인지

  2. 객체지향 프로그래밍의 특징중 하나인 상속이 프로토타입을 기반으로 어떻게 구현이 되고 있는지

  3. 상속을 구현하는데 프로토타입 객체와 __proto__ 접근자 프로퍼티 가 어떻게 사용되는지

  4. String, Array, Date, Math 표준 빌트인 객체와 그에 따른 메서드들에 대해

  5. 추가로 금일 교육중 좀더 알아보고 싶었던 주제들에 대해 조사

📗 자바스크립트와 프로토타입

자바스크립트는 객체 기반의 프로그래밍 언어이며 자바스크립트의 거의 모든 것이 객체이다. (원시 값 제외한 나머지 값들)

객체지향 프로그래밍 : 실세계의 실체를 여러 개의 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임이다.

실체는 특징이나 성질을 나타내는 속성(attribute/property) 을 지니고 있고 그중 필요한 속성만 간추어 내어 표현하는 것을 추상화(abstraction) 라고 한다.

예를 들어보자

원이라는 객체가 반지름을 지니고 있고 지름을 구할 수 있다고 해보자. 이를 코드로 나타내면 다음과 같다.

const circle = {
	radius: 5, // 반지름
  
  	// 원의 지름: 2r
  	getDiameter() {
    	return 2 * this.radius;
    }
}

위의 예시에서 볼 수 있는 객체지향 프로그래밍의 특징으로 객체의 상태 status 를 나타내는 radius 라는 데이터와 데이터를 조작할 수 있는 동작을 하나의 단위로 취급한다.

이때의 상태프로퍼티 property, 동작메서드 method 라고 부른다.


상속 은 객체지향 프로그래밍의 핵심 개념으로 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.

모던 자바스크립트 Deep Dive 에서 소개되고 있는 상속의 예시를 참고했다.

상속을 고려하지 않은 상태의 예시이다.

위에서 Circle 생성자 함수가 생성하는 모든 객체(인스턴스) 는 radius property 와 getArea method 를 갖는다. 문제는 getArea 는 항상 동일한 내용인데 중복 생성된다는 점이다. 인스턴스가 생성되며 불필요한 메모리 공간을 차지한다는 의미이다.


생성자 함수 (constructor function) 은 일반 함수와 기술적 차이는 없으나 다음 두 관례를 따른다.
1. 함수 이름의 첫 글자는 대문자로 시작
2. 반드시 new 연산자를 붙여 실행

function Circle(radius) {
	this.radius = radius;
}

let circle = new Circle(5);

new Circle 을 사용하여 함수를 실행시

  1. 빈 객체를 만들어 this 에 할당
  2. 함수 본문을 실행, this 에 새로운 프로퍼티를 추가해 this 수정
  3. this 반환
function Circle(radius) {
  	// this = {}; 빈 객체가 임시적으로 생성
  
  	// 새 프로퍼티를 this 에 추가
	this.radius = radius;
  
  	// return this; this 가 암시적으로 반환된다.
}

그럼 상속을 사용한다면?

자바스크립트는 프로토타입을 기반으로 상속을 구현한다.
[TMI] 지금 굳이 프로토타입을 언급해가며 이 부분을 설명하는 까닭은 아래 표준 빌트인 객체인 String, Array, Math, Date 와 이들의 메서드를 설명하는데 관련이 있기 때문이다.

프로토타입을 기반으로 상속을 구현한 사례를 보자. 지난 포스트 [포스코x코딩온] KDT-Web-8 2주차 회고1 - Javascript 에서 프로토타입을 설명했을때와 같이

Circle 이라는 객체는 Circle's prototype 이라는 프로토타입 객체를 갖는다. 이 프로토타입 객체가 Circle.prototype 이다. 이 프로토타입 객체에 이전에 존재하지 않던 getArea 라는 메서드를 추가하니 Circle 이라는 생성자 함수로 새로운 circle 인스턴스를 아무리 만들든 이 인스턴스는 radius 라는 property 만 갖는다.

이때 circle1.getArea 를 실행하려 하니 이 인스턴스는 getArea 라는 메서드를 갖고 있지 않으니 __proto__ 가 참조하고 있는 Circle's prototype 을 찾아가 getArea 를 실행하는 것이라 보면 된다.

이때의 장점은 인스턴스를 만들 때마다 getArea 를 위한 공간을 불필요하게 만들 필요 없이 Circle's prototype 프로토타입 객체에 한번만 getArea 를 생성해두면 얼마든지 가져다 사용할 수 있게 된다.

이 getArea 사용법 익숙하지 않은가..? 오늘 계속 사용했던 length, split() 이런건 어디서 나왔을까? 바로 표준 빌트인 객체의 프로토타입 객체가 갖고 있던 메서드가 아니었을까? 확인해보자.

상속을 사용하지 않을 때상속을 사용할 때

객체를 기반으로 생성한 인스턴스는 프로토타입 체인의 일원이 된다.

📗 메서드

클래스 몸체에서 정의할 수 있는 메서드 3종류에 대해 알아보자

📕 constructor(생성자)

인스턴스를 생성하고 초기화하기 위한 메서드

class Circle {
	constructor(radius) {
    	this.radius = radius;
    }
}

📕 프로토타입 메서드

프로토타입 객체에 추가하는 메서드

function Circle(radius) {
  this.radius = radius;
}

// 프로토타입 메서드
Circle.prototype.getArea = function (radius) {
	return Math.PI * this.radius ** 2;
}

📕 정적 메서드

인스턴스를 생성하지 않아도 호출할 수 있는 메서드를 말한다.

  1. 정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.
  2. 정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.
  3. 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.

생성자 함수인 표준 빌트인 객체가 생성한 인스턴스의 프로토타입은 표준 빌트인 객체의 prototype 프로퍼티에 바인딩된 객체이다.

  • ex) 표준 빌트인 객체인 String 을 생성자 함수로서 호출하여 생성한 String 인스턴스의 프로토타입은 String.prototype 이다.
  • ex) String 의 prototype 프로퍼티에 바인딩된 객체 String.prototype 은 다양한 빌트인 프로토타입 메서드를 제공하며 이 프로토타입 메서드는 모든 String 인스턴스가 상속을 통해 사용할 수 있다.

이제 맨 처음 길게 말했던 내용이 조금은 이해가 되었으리라 생각한다.

Math, Reflect, JSON 표준 빌트인 객체그외 표준 빌트인 객체
인스턴스를 생성할 수 없음인스턴스를 생성할 수 있음(생성자 함수 객체)
정적 메서드만 제공프로토타입 메서드정적 메서드를 제공

예외사항) 임시 객체 (Wrapper object)

  • 문자열, 숫자, 불리언 같은 원시값은 객체가 아닌데 오늘 수업때 보면 문자열을 마치 객체처럼 .length 로 접근하던 것을 기억할 것이다.

문자열, 숫자, 불리언 값에 대해 객체처럼 접근하면 생성되는 임시 객체
(래퍼 객체)로 프로퍼티에 접근하거나 메서드를 호출한 후, 다시 원시값으로 되돌린다.

📗 String

표준 빌트인 객체인 String 객체는 생성자 함수 객체로 new 연산자와 함께 호출하여 String 인스턴스를 생성할 수 있다.

String 래퍼 객체는 length 프로퍼티와 인덱스를 나타내는 숫자 형식의 문자열을 프로퍼티 키로 각 문자를 프로퍼티 값으로 갖는 유사배열 객체이며 이터러블이다. 따라서 배열처럼 인덱스를 사용해서 각 문자에 접근할 수 있다.

배열에는 원본배열을 직접 변경하는 메서드(mutator method) 와 원본 배열을 변경하지 않고 새로운 배열을 생성해서 반환하는 메서드(accessor method) 가 있는데 String 객체는 언제나 새로운 배열을 생성해서 반환한다.

String 은 read only 읽기전용으로 변경 불가능하니 언제나 새로운 문자열을 생성해서 반환한다.

📕 문자열 관련 메소드

아래에 표시한 메서드들은 사실 String.prototype.[메서드명] 임을 알아둬야 한다. 즉, 표준 빌트인 객체인 String 의 프로토타입 메서드에 다음과 같은 메서드들이 생성되어 있으니 생성자 함수로 인스턴스를 생성해도 불필요하게 중복 생성될 필요 없이 프로토타입 메서드를 상속받아 사용하는 것이다.

아래의 모든 문자열 관련 메서드가 프로토타입 메서드이다!

str 이라는 인스턴스에 indexOf 라는 메서드가 없다고 하더라도 프로토타입 체이닝에 의해 거슬러 올라가다 보면 프로토타입 객체에 있는 indexOf 를 사용할 수 있게 된느 것이다.

str.length : 문자 개수를 반환 (프로토타입 메서드가 아닌 프로토타입 프로퍼티)
str.toUpperCase() , str.toLowerCase() : 대상 문자열을 모두 대문자로, 소문자로 변경하여 반환한다.
str.indexOf('x') :

  • str 에서 x 를 검색해서 첫 번째 인덱스를 반환하고 없으면 -1 을 반환한다.
  • 두번째 인수로 검색을 시작할 인덱스를 전달할 수 있다.
  • 대상 문자열에 특정 문자열이 존재하는지 확인시 유용하다.

str.search(/o/) : 정규 표현식과 매치하는 문자열을 검색하고 일치하는 문자열의 인덱스를 반환한다. 실패시 -1 을 반환한다.
str.includes('Hello') :

  • ES6 에서 도입, 대상 문자열에 인수로 전달받은 문자열이 포함되어 있는지 여부를 불린형으로 반환한다.
  • 두번째 인수로 검색을 시작할 인덱스를 전달할 수 있다.

str.startsWith(), str.endsWith() :

  • 대상 문자열로 시작하는지, 끝나는지 여부를 불린형으로 반환한다.
  • 두번째 인수로 검색을 시작할 인덱스를 전달할 수 있다.

str.charAt() : 인수로 전달받은 인덱스에 위치한 문자를 검색해서 반환
str.substring(1, 4) : 대상 문자열에서 첫 번째 인덱스 위치 문자부터 두번째 인덱스 이전 문자까지의 부분 문자열을 반환한다.
str.slice() :

  • substring 과 동일하게 동작한다. 단, 음수인 인수를 전달할 수 있다.
  • 이 경우 대상 문자열의 가장 뒤에서부터 시작해서 문자열을 잘라내어 반환한다.

str.replace('str', 'target') : 첫 번째 인수로 전달받은 문자열 또는 정규표현식을 검색해서 두 번째 인수로 전달한 문자열로 치환한 문자열을 반환한다.
str.repalceAll()
str.repeat() : 대상 문자열을 인수로 전달받은 정수만큼 반복해 새로운 문자열을 반환한다.
str.split('' , 2) : 첫 번째 인수로 전달한 문자열 또는 정규 표현식을 검색해 문자열을 구분한 뒤 분리된 각 문자열로 이루어진 배열을 반환하며 기본은 ',' 이다.

str.trim() : 대상 문자열 앞뒤 공백 문자를 제거한 문자열을 반환한다.

📗 Array

📕 배열 생성

📕 배열 관련 메소드

Array 생성자 함수는 정적 메서드를 제공하며 배열 객체의 프로토타입인 Array.prototype 은 프로토타입 메서드를 제공한다.

아래의 예시중 Array.isArray, Array.of, Array.from 만 정적 메서드이고 나머지는 모두 프로토타입 메서드이다.

앞에서 설명했듯

배열에는 원본배열을 직접 변경하는 메서드(mutator method) 와 원본 배열을 변경하지 않고 새로운 배열을 생성해서 반환하는 메서드(accessor method) 가 있다.

  • mutator method 는 side effect 의 우려가 있으니 사용시 주의해야 하며 accessor method 를 사용하는 편이 좋다.

📖 mutator method (원본 배열을 직접 변경하는 메서드)

array.push() : 배열 끝에 새 element 추가

array.pop() : 원본 배열의 마지막 요소를 제거하고 제거한 요소를 반환한다.

array.unshift(): 인수로 전달받은 모든 값을 원본 배열의 선두에 요소로 추가하고 변경된 length 프로퍼티 값을 반환한다.

unshift 는 부수효과가 발생하니 이 대신 스프레드 문법을 사용하는 편을 권장한다.

array.shift() : 원본 배열에서 첫 번째 요소를 제거하고 제거한 요소를 반환한다.

array.splice(start, deleteCount, items) :

  • 원본 배열의 중간에 요소를 추가하거나 중간에 있는 요소를 제거한다.
  • start : 제거하기 시작할 인덱스
  • deleteCount : start 부터 제거할 요소의 개수
  • items : 제거한 위치에 삽입할 요소들의 목록

array.reverse() : 원본 배열의 순서를 반대로 반전시킵니다.

array.fill() :

  • 인수로 전달받은 값을 배열의 처음부터 끝까지 요소로 채운다.
  • 두 번째 요소로 요소 채우기를 시작할 인덱스를 전달할 수 있다.
  • 세 번째 요소로 요소 채우기를 멈출 인덱스를 전달할 수 있다.

array.sort() :

  • 배열의 요소를 정렬한다. 기본적으로 오름차순이다.
  • 내림차순으로 정렬을 하려면 reverse 를 활용한다.

📖 accessor method (새로운 배열을 생성하는 메서드)

array.concat : 배열 끝에 새 element 추가

array.slice(start, end) :

  • 인수로 전달된 범위의 요소들을 복사하여 배열로 반환한다.
  • start : 복사를 시작할 인덱스
  • end : 복사를 종료할 인덱스

array.map() :

  • 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출한다.
  • 콜백 함수의 반환값들로 구성된 새로운 배열을 반환하며 원본 배열은 변경되지 않는다.

array.filter() :

  • 자신을 호출한 배열의 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출한다.
  • 콜백 함수의 반환값이 true 인 요소들로만 구성된 새로운 배열을 반환한다.

array.length : 배열의 길이를 나타내는 0 이상의 정수를 값으로 갖는다.

array.indexOf() :

  • 원본 배열에서 인수로 전달된 요소를 검색해서 인덱스를 반환한다.
  • 찾지 못하면 -1을 반환한다.

array.includes() :

  • 배열 내에 특정 요소가 포함되어 있는지 여부를 불린형으로 반환한다.
  • 두 번째 요소로 검색을 시작할 인덱스를 전달할 수 있다.
  • 두 번째 인수에 음수 전달시 length 프로퍼티 값과 음수 인덱스를 합해서 검색 시작 인덱스를 설정한다.

array.join() : 원본 요소의 모든 요소를 문자열로 변환한 후, 인수로 전달받은 구분자로 연결한 문자열을 반환한다. 기본 구분자는 , 이다.

array.forEach() :

  • 반복문을 추상화한 고차 함수로서 내부에서 반복문을 통해 자신을 호출한 배열을 순회하며 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출한다.
  • 반환값은 언제나 undefined 이다.
  • 기본적으로 원본 배열을 변경하지 않으나 콜백 함수를 통해 원본 배열을 변경할 수 있다.

push 와 unshift 메서드는 원본 배열을 직접 변경하나 concat 은 원본 배열을 변경하지 않고 새로운 배열을 반환한다.

pop, push 메서드를 사용하여 스택을 구현하거나 (LIFO) shift, push 메서드를 사용하여 큐를 구현할 수 있다. (FIFO)

side effect : 부수효과 : 함수에서 결과값을 주는 것 외에 하는 행동을 말한다.

  • 이메일 전송
  • 파일 읽기
  • 불빛 깜빡이기
  • 웹 요청하기
  • 자동차에 브레이크 밟기

📗 Date

표준 빌트인 객체인 Date 는 날짜, 시간을 위한 메서드를 제공하는 빌트인 객체이면서 생성자 함수이다.

1970년 1월 1일 00:00:00(UTC) 기준

UTC (Coordinated Universal Time) : 국제 표준시
KST (Korea Standard Time) : 한국 표준시

📕 new Date()

현재 날짜와 시간을 가지는 Date 객체를 반환한다.

📕 Date.now()

1970년 1월 1일 00:00:00(UTC)을 기점으로 현재 시간까지 경과한 밀리초를 반환한다.

📗 Math

수학적인 상수와 함수를 위한 프로퍼티와 메서드를 제공한다.

📘 개인학습

📗 Spread 문법

ES6 에서 도입된 Spread 문법 ... 은 하나로 뭉쳐 있는 여러 값들의 집합을 펼쳐서 개별적인 값들의 목록으로 만든다. 단, 이는 값이 아니라 값들의 목록이라 스프레드 문법의 결과는 값처럼 변수에 할당할 수 없고 다음과 같이 쉼표로 구분한 값의 목록을 사용하는 문맥에서만 사용할 수 있다.

📕 함수 호출문의 인수 목록

rest parameter 는 함수에 전달된 인수들의 목록을 배열로 전달받기 위해 매개변수 이름 앞에 ...을 붙이는 것이다. 스프레드 문법은 여러 개의 값이 하나로 뭉쳐있는 배열과 같은 이터러블을 펼쳐서 개별적인 값들의 목록을 만드는 것이다.

📕 배열 리터럴의 요소 목록

📕 객체 리터럴의 프로퍼티 목록

📗 Deep copy, shallow copy

객체를 프로퍼티 값으로 갖는 객체의 경우 얕은 복사는 한 단계까지만 복사하는 것을 말하고 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사하는 것을 말한다.

  • 얕은 복사는 객체에 중첩되어 있는 객체의 경우 참조 값을 복사한다.
    따라서 원본에 수정이 들어가도 복사본에도 중첩되어 있는 객체의 참조값을 공유하므로 같은 변화를 보인다.

  • 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만든다.
    완전한 복사본이니 원본에 변경이 있든 상관이 없이 완전히 다른 복사본을 생성한 것이다.

원시 값을 할당한 변수를 다른 변수에 할당하는 것을 깊은 복사, 객체를 할당한 변수를 다른 변수에 할당하는 것을 얕은 복사라고도 한다.

배열의 slice 메서드, 스프레드 문법, Object.assign 메서드는 얕은 복사를 수행한다. 깊은 복사를 위해서는 Lodash 라이브러리의 cloneDeep 메서드를 사용하길 권장한다.

📓 결론

  • 메서드는 객체의 동작을 의미한다
  • 메서드는 생성자함수, 프로토타입 객체중 어디에 있는지에 따라 정적 메서드와 프로토타입 메서드로 구분할 수 있다.
  • 자바스크립트 표준 빌트인객체인 String, Array, Date, Math 는 프로토타입 객체에 있는 프로토타입 메서드를 상속받아 생성자 함수로 생성한 인스턴스를 통해 사용할 수 있다.
  • 배열 기반 메서드는 원본 변경 여부와 새 배열을 반환하는지에 따라 mutator method 와 accessor method 로 나눌 수 있다.

프로토타입, Destructuring, Spread, DOM, 프로토타입 메서드중 forEach, map, flatMap, filter 에 대해 추가적인 학습의 필요성을 느꼈다. 특히 forEach, map, filter 는 객체, class 에 대해 학습을 한뒤 반드시 다시 정리해보자.

이번 포스트를 작성하며 표준 빌트인 객체가 갖는 프로토타입 메서드, 정적 메서드에 관해 확실히 알게 되었다. 정적 메서드의 경우 생성자 함수에 속해 있으니 프로토타입 메서드처럼 프로토타입 체이닝에 적용되지 않는다는 점을 주의해야 할 것 같다.

깊은 복사와 얕은 복사도 원래 얕은 복사가 1단계만 복사하고 깊은 복사가 nested 구조까지 모두 복사한다고만 알고 있었는데 실제로는 얕은 복사는 내부에 객체가 있을 경우 참조값을 복사하니 원본의 변화를 공유할 수 있다는 것과 깊은 복사가 완전한 복사를 할 경우 원본에 영향 없이 완전히 동일한 새로운 객체를 생성한다는 점을 알게 되었다.

머리가 거부하려해도 지식을 밀어넣고 있다... 🫠

📔 레퍼런스

모던 자바스크립트 Deep Dive
인프런 모던 자바스크립트 Deep Dive 스터디
쏙쏙 들어오는 함수형 코딩

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글