Monaq Devlog

  • Home

  • Archives

Observable 을 구현하면서 Observable 배우기

Posted on 2019-01-12 | Edited on 2019-01-13

Observable 을 구현하면서 Observable 배우기

Safe Observer :

Observable은 단지 function일 뿐이다. subscribe를 할때까지 어떤 기능도 하지 않는다.
사용자의 observer를 구성하고 끝나며, call되기를 기다리는 이전 function으로 돌아간다.
반면 observer는 동적이며 생성자로부터의 이벤트를 듣고 있다.

observer가 보장하는 것들

  • 만약 Observer에 모든 메소드가 구현되어 있지 않은 채 전달되어도 괜찮다.
  • unsubscribe 후에는 아무것도 call하지 않는다.
  • complate나 error 후에 next를 호출하고 싶지는 않을 것이다.
  • complate와 error를 call하는 것은 unsubscribe로직을 필요로 한다.
  • next, complate나 error 핸들러가 예외를 던진다면, unsubscribe 로직을 호출함으로서 리소스 낭비를 줄일 수 있다.
  • next, error, complate는 사실 옵셔널하다. 모든 밸류나 에러를 핸들링할 필요 없으며 한 가지나 두 가지만 핸들링해도 괜찮다.



    위를 보장하기 위해 safeObserver로 익명의 observer를 감싼다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// safe observer 구현체
class SafeObserver {
constructor(destination) {
this.destination = destination;
}
next(value) {
if (!this.isUnsubscribed && this.destination.next) {
try {
this.destination.next(value);
} catch (err) {
this.unsubscribe();
throw err;
}
}
}

error(err) {
if(!this.isUnsubscribed && this.destination.error) {
try {
this.destination.error(err);
} catch (e2) {
this.unsubscribe();
throw e2;
}
this.unsubscribe();
}
}

complete() {
if(!this.isUnsubscribed && this.destination.complete) {
try {
this.destination.complete();
} catch (err) {
this.unsubscribe();
throw err;
}
this.unsubscribe();
}
}

unsubscribe() {
this.isUnsubscribed = true;
if (this.unsub) {
this.unsub();
}
}
}

자바스크립트의 언어적 개념 9

Posted on 2019-01-09 | Edited on 2019-01-13

오브젝트

9.1 프로퍼티 리스트

  • 책 참고.

    9.2 Object 분류

  • 오브젝트: 0개 이상의 프로퍼티로 구성되는 오브젝트
  • 빌트인 오브젝트: 렌더링 과정에서 자바스크립트가 생성하는 오브젝트
  • 네이티브 오브젝트: 자바스크립트 프로그램을 실행할 때 생성되는 오브젝트
  • 호스트 오브젝트: 호스트 환경에서 자바스크립트 실행 환경을 보완, 지원하기 위해 제공하는 오브젝트(ex. DOM에서 제공하는 오브젝트)

9.3 new Object()

  • 인스턴스를 생성하여 반환한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var obj = new Object();
    console.log(obj); // [object Object]

    var numObj = new Number(123);
    var objNumber = new Object(numObj);
    console.log(numObj === objNumber); // true

    var obj = new Object(1234);
    console.log(typeof obj); // object
    console.log(obj); //1234

    console.log(new Object(true)); //true
    var nodes = document.getElementByTagName('script');
    console.log(new Object(nodes[0])) // [object HTMLScriptElement]
  • 책 참고

    9.4 Object()

  • Object() 와 new Object()로 생성한 겂은 같다.

    9.5 문자열 표시 반환

  • toString() 은 오브젝트 타입을 문자열 표시 형태로 변환해 반환한다.
    1
    2
    3
    4
    5
    6
    console.log(Object().toString());

    var obj = new Object();
    console.log(obj.toString.call(new Number())); // [object Number]
    console.log(obj.toString.call(Undefined)); // [object Undefined]
    console.log(obj.toString.call(null)); // [object Null]

toString() 의 특징

1
2
var obj = new Number(123);
console.log(obj.toString()); //123

상속을 받았는데 빌트인 오브젝트의 toString()메소드가 호출되지 않는 것은 각 오브젝트에 toString 메소드가 있기 때문이다.(만일 이렇다면 원시타입을 반환하겠죠?)
toString()메소드가 호출되면 각 오브젝트에 속한 toString()메소드를 먼저 찾는다.

9.6 지역화 문자로 변환

  • 지역화 문자로 변환한 값 반환
    ex) IE브라우저로 한국에서 12345 변환 => 12,345.00

    9.7 프리미티브 값 반환

  • object 위치에 인스턴스를 지정하며 인스턴스의 원시 값을 반환한다.
  • Boolean, Date, Number, String오브젝트는 [[PrimitiveValue]]값이 반환되도록 하기 위해 오브젝트에 valueOf메소드를 갖고 있다.

    9.8 프로퍼티 소유 여부

  • hasOwnProperty 메소드는 프로퍼티 소유여부를 Boolean으로 반환
    1
    2
    3
    var obj = Object();
    ojb.pty = 'check';
    console.log(obj.hasOwnProperty('pty')); //true

prototype chain, 상속

prototype에 오브젝트를 연결하는 것과 인스턴스를 연결하는 것은 다르다.
오브젝트 연결은 [name: value]형태를 연결한 것이고 인스턴스 연결은 new 연산자로 생성한 인스턴스를 연결한 것이다.
인스턴스가 연결된 형태를 prototype chain이라고 한다.

9.9 prototype에 오브젝트 존재 여부

isPrototypeOf 메소드는 prototype에 오브젝트가 존재하는지 여부를 Boolean값으로 반환

9.10 프로퍼티 열거 가능 여부

propertyEnumerable 메소드는 프로퍼티의 열거 가능 여부를 반환한다.

1
2
3
4
5
6
7
8
9
10
var obj = new Object();
obj.value = '값';
console.log(Object.keys(obj)); // [value]
console.log(obj.propertyIsEnumerable('value')); // true

Object.prototype.add = 'plus';

var addObj = new Object();
console.log(addObj.add); // plus
console.log(addObj.propertyIsEnumerable('add')); // false

자바스크립트의 언어적 개념 8

Posted on 2019-01-02 | Edited on 2019-01-13

자바스크립트의 언어적 개념

8.1 자바스크립트 기준과 범위

자바스크립트 기준과 범위

자바스크립트는 객체지향 프로그래밍(OOP: Object Oriented Programming) 언어이다. 객체 지향 프로그램을 구현하는 방법은 언어마다 차이가 있다. 사전 컴파일 언어와 스크립트 언어는 환경이 다르므로 구현 방법이 다르므로 자바스크립트의 경우 OOP의 일부 개념을 지원하지 않는다.

그러나 최근 자바스크립트에서도 OOP의 개념을 사용하여 개발하고 있으므로 알아 두는 것이 좋겠다.

OOP의 기본 개념

  • 캡슐화: 외부에서 접근하지 못하게 private 화 하는 것
  • 다형성: 상속을 통해서 다른 동작을 가능하게 해 준다. 오버라이딩/오버로딩을 통해 가능하다. 부모클래스에서 상속받은 속성을 재창조해서 사용하는 것이 다형성이다. 같은 행위를 목적에 맞게 다양한 기능 수행을 할 수 있도록 바꾸는 것.
  • 추상화: 추상 클래스를 만든다. 개발할 요소들을 모두 클래스에 담고 재사용할수 있게 만들 수 없다. 공통적인 메소드를 추출하여 추상화 하는 것이 추상화이다.
  • 상속성: 기존 클래스의 기능을 가져와 만들면서도 새롭게 만든 클래스에 새로운 기능을 추가할 수 있는 것.

객체

  • 행위(Behavior)와 속성(Attribute)을 갖고 있고 실체가 존재한다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    사람 = {
    eat: function(fruit) {
    eat fruit();
    },
    quantity: 1
    }
    // eat 는 과일을 먹는 '행위'를 나타내며 quantity는 과일 1개를 나타내는 이름이다
    // eat에 접근하면 먹는 행위를 실행할 수 있고
    // quantity에 접근하면 과일 1개 값을 구할 수 있다.

프로퍼티

1
2
3
sports = [soccor: '11']
// 11을 구하려면 우선 오브젝트인 sports를 알아야 한다.
// 다른 오브젝트, 예를 들어 game = [soccer: '11'] 과 같이 존재할 수 있기 때문
  • 오브젝트에는 프로퍼티 이름이 하나만 존재해야 한다.
  • es5 스트릭트 모드에서는 오브젝트에 같은 이름이 있으면 오버라이딩 된다.

    오브젝트와 인스턴스

  • object 를 사용하여 Object를 생성하면 Object가 반환됨
  • new 연산자를 사용하여 오브젝트를 생성하여 변수에 할당할 수 있으며, 이 할당된 오브젝트가 인스턴스 이다.
  • 인스턴스를 생성하는 목적: 인스턴스마다 다른 값을 유지하고, 제어하기 위해 인스턴스를 생성한다.

    함수와 메소드

  • 함수는 오브젝트에 속하며, 행위를 나타낸다.
  • 함수 안에 함수의 목적 달성을 위한 코드를 작성한다.

8.2 빌트인

  • 자바스크립트를 컴파일하고 생성하는 단계.
  • 빌트인의 목적: 개발자 프로그램이 실행되기 전에 미리 생성되어 프로그램에서 사용할 수 있게 환경을 만들어주는 역할임.

    빌트인 연산자

    빌트인 데이터 타입

  • Undefined, Null, Boolean, Number, String

    빌트인 오브젝트

  • Global, Object, Function, Array, String, Boolean, Number
  • Math, Date, RegExp, JSON
  • Error 처리용 오브젝트

8.3 빌트인 오브젝트

  • Global,
  • Object,
  • Function,
  • Array,
  • String,
  • Boolean,
  • Number

  • Math

  • Date
  • RegExp
  • JSON

Object 인식

객체지향 형식

1
2
3
4
5
6
7
var obj = new String();
var result = obj.concat('sport', 'soccer', 11);
console.log(result); //sportssoccer11

obj = new Array();
result = obj.concat('sports', 'soccer',11);
console.log(result); // ['sports', 'soccer', 11]

자바스크립트 형식

1
2
3
4
5
var result = 'sports'.concat('soccer', 11);
console.log(result); //sportssoccer11

result = ['sports'].concat('sports', 'soccer',11);
console.log(result); // ['sports', 'soccer', 11]
  • 이처럼 데이터 타입에 의해 오브젝트에 속한 메소드가 호출되는 메커니즘을 뒷받침하는것이 빌트인 타입, 빌트인 오브젝트이다.

8.5 prototype 오브젝트

1
2
3
4
5
6
// concat 메소드를 오브젝트 형태로 표현
String: {
length: 값,
concat: function() {},
indexOf: function() {}
}
  • 위와 같이 작성하면 각각의 프로퍼티와 메소드가 구분되지 않기 때문에 prototype을 사용하여 메소드를 구분한다.
    1
    2
    3
    4
    // concat 메소드를 prototype 형태로 표현
    String.length
    String.prototype.concat
    String.prototype.indexOf
1
2
3
4
5
6
7
8
// concat 메소드를 prototype 형태로 표현
String: {
length: 값,
prototype: {
concat: function() {},
indexOf: function() {}
}
}
  • 프로토타입은 이외에더 다른 오브젝트를 상속받아 연결하거나 prototype에 연결된 프로퍼티를 공유할 때 사용함.(OOP에서의 상속)

8.6 new 연산자

1
2
3
var str = new String(); // 스트링 객체 반환
var Sports = function(){};
var spts = new Sports(); // function 객체 반환
  • new 연산자는 인스턴스를 생성하여 반환한다.
  • Number는 오브젝트를 생성하지 않고 Number(‘123’)일때 문자열 123을 숫자로 변환하여 반환 한다.

    fallback (pollyfill)

  • 크로스브라우징을 할 때 같은 이름의 메소드를 만들어 빌트인 오브젝트의 prototype에 연결하면 된다.

    8.7 constructor

  • constructor가 있어야 새로운 오브젝트(인스턴스)를 생성할 수 있다.

8.8 instanceOf 연산자

1
2
3
var obj = new Object();
console.log(obj == Object); //false
console.log(obj instanceof Object) //true
  • 인스턴스의 오브젝트 형식을 비교하려면 instanceOf 연산자를 사용한다.

git 운용 전략

Posted on 2018-04-02 | Edited on 2019-01-13

고민

  1. kakao.ai를 인수인계 받은 후 처음 보는 git 운용법에 익숙해지는 것이 늦었음.
  2. 너무 잦은 기획/디자인 변경과 상시 배포에 혼란을 겪음
  3. 인수인계자의 방법을 연구해 나름의 컨벤션을 재정립하기로 함

커밋 컨벤션

  • [update] - : 기획적 목적에서의 배포를 위한 기능 구현 커밋

  • [test] - : 개발적 측면에서의 테스트용 커밋

  • [delete] - : directory 삭제

  • [fix] - : freezing 된 코드에 대한 커밋

  • [hotfix] - : 버그 수정을 위한 긴급 배포용 커밋

배포 방식

  1. 배포예정 월일 기준으로 브랜치를 만듦 : update_MM_DD_release 와 같은 네이밍 컨벤션 사용.

  2. update_MM_DD_release 브랜치는 develop 브랜치에서 나와 develop 브랜치로 들어간다.

  3. update_MM_DD_release 에서 개발이 끝나면 기획자 검수를 위해 해당 브랜치 기준으로 개발 서버에 배포.

  4. 1차 QA후 무한 [update] 반복… [fix] 된 코드를 develop에 머지하고 최종 QA

  5. 최종 QA 후 develop의 코드를 master에 머지 하고 스테이지 서버에 배포

  6. 실배포 이후 [hotfix] 발생시 master에서 [hotfix] 브랜치를 따서 수정 후 master, develop에 머지

코드 히스토리를 찾아야 할 떄는

  • 해당 배포 년월일의 브랜치로 이동한다.
  • 커밋 컨벤션을 참고하면서 목적에 맞는 로그를 찾는다.
  • [fix] 커밋만 골라서 보면 더 빠르게 히스토리 정리가 가능하다.

추후 계획

  • 리액트 코드리뷰 전략이 짜여지면 pull request를 이용한 방식을 차용할 생각
  • 피드백이 필요한 코드가 있거나 머지 직전인 경우 pull request를 열어 코드리뷰를 받는다.
  • 현재 프로젝트 상황처럼 hotfix가 잦거나 디자인 변경같은 버그 가능성이 낮은 배포가 잦으면 pull request를 강제하는 것이 좋지 않을 것.

-> 필요한 상황에서만이나 주기적으로 코드리뷰를 받도록 규칙을 정해야 한다.

redux

Posted on 2018-03-25 | Edited on 2019-01-06

redux 간단개요

학습목표

리액트 필터링 기능 App.js예제에서 동작 원리를 알아본다.


Flux 패턴은 라이브러리가 아닌 하나의 설계 방법론일 뿐이다.

parent-child 관계가 아닌 각각의 컴포넌트끼리 데이터를 교류하기 위해 글로벌 이벤트시스템을 설정하는 방법.

App.js

1. Action 만들기.

action: 무슨 변화를 일으켜야 하는지 알려주는 객체.

1
2
3
4
5
6

function setFilter(by) {

return {type: 'SET\_FILTER', by};

}

2. reducer 만들기.

reducer: action으로부터 정보를 받아서 앱 상태를 어떻게 바꿀지 정의하는 객체. (action을 subscribe하고 있는놈)

초기값 설정

1
2
3
4
5
6

const initialState = {

filterBy: ''

}

리듀서 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function reducer(state = initialState, action) {

switch (action.type) {

case 'SET\_FILTER':

console.log(\`Our filter was ${state.filterBy} but is now ${action.by}!`);

return Object.assign({}, state, {

filterBy: action.by

})

default:

return state

}

}
  • 순수함수인 reducer는 반드시 parameter값에만 의존되어야 함.

3. store 생성

store: 앱의 state를 총괄적으로 관리하는 Single Source of Truth

1
2

const store = createStore(reducer)

store생성시 Root Reducer를 담아 전달함

여기서는 리듀서가 하나뿐이지만, 리듀서가 여러개일 경우 combineReducers()로 한개의 root reducer를 만들어 전달한다.

4. connect

->리액트 컴포넌트를 redux store에 매핑 시키는 api

store의 state를 컴포넌트에서 받은 props에 매핑


const mapStateToProps = (state) => {

  return {

    filterBy: state.filterBy

  }

}

컴포넌트의 어떤 함수형 props를 실행했을 때 특정한 action을 dispatch하도록 설정

1

2

3

4

5

const mapDispatchToProps = (dispatch) => {

return {

updateFilter: (ev) => dispatch(setFilter(ev.target.value))

}

}

세미콜론, 그 끝나지 않는 논쟁

Posted on 2018-01-28 | Edited on 2019-01-06

세미콜론

세미콜론 성대 페북세미콜론 성대 페북
[사진 성균관대학교 대나무숲 페이스북 캡처]

자바스크립트에서의 세미콜론(;) 사용에 대한 논쟁이 갑자기 다시 불이 붙는 이유는 여기서 알 수 있었다.
semi-colon 사용을 의무화하는 것을 고려중인 TC39

as new syntactic features are added to ECMAScript, additional cases requiring explicit semicolons emerge over time. As such, consistently explicit semicolon use is recommended.
새로운 문법이 ECMA 스크립트로 추가되는 과정에서 명시적으로 세미콜론을 사용해야 하는 경우가 나타나고 있다. 그러므로 일관성 있게 세미콜론을 명시적으로 사용하는 것을 권고한다.

(https://jayjnu.github.io/2018/01/24/2018-jan-3rd-week/ 에서 인용)

풀리퀘가 하도 많아서 접속 장애가 일어나는 경우는 처음 봤다.;;
자바스크립트에서의 세미콜론 사용에 대한 이야기는 끝나지 않는 논쟁이긴 하지만 매우 접근이 쉽고 재미있는 논쟁거리이기도 하다.
아직 경험에서 우러나오는 주장을 펼치기에 부족한 감이 있지만 알고 있는 요점만 정리해서 글을 읽는 여러분에게 선택을 맡기도록 하려고 한다. 참고로 나는 앞으로 세미콜론을 쓸 것이다.

자바스크립트에서는 세미콜론을 빼도 상관없다?

자바스크립트에서는 세미콜론을 생략할 수 있는 점이 매우 편리하다.

1
2
3
var i = 0; i++

// 세미콜론이 꼭 필요한 경우
1
2
3
4
5
var i = 0

i++

// 세미콜론이 들어가도 되고 안 들어가도 되는 경우

이는 ECMAScript 표준에서 지원하는 자동 세미콜론 삽입 덕분이다. 이는 특정 문맥에서 생략된 세미콜론을 추론하여 파싱해 주는 매커니즘이다.
세미콜론 자동 삽입 규칙은 다음과 같다.

  • 한 줄 이상의 새로운 행
  • 프로그램 입력의 끝부분
  • } 토큰 전

다시 말하면 이 경우 외에는 세미콜론을 명시해 줘야 한다는 뜻이다.

또 다른 규칙을 보자.

  • 세미콜론은 다음 입력토큰이 존재하지 않을 경우에만 파싱된다.
1
2
3
a = b

( f() )

위 코드는 문제없다.

1
2
3
a = b

f ()

위 코드는 문제가 된다.
다음과 같이 해석되기 때문이다.

1
a = b f ()

다음과 같은 배열 리터럴의 경우도 문제가 된다.

1
2
3
4
5
6
7
a = b

["x","y","z"].forEach(function(key) {

...

}

다음과 같이 파싱되기 때문이다.

1
2
3
4
5
a = b["x","y","z"].forEach(function(key) {

...

}

그래서 자동 세미콜론 삽입기능의 도움을 받으려면 (, [, +, -, / 등의 표현식 연산자나 접두어 토큰을 쓸 때 조심해야 한다. 삽질의 사직이 될 수 있음.

위의 경우를 제외하면, 또는 완벽하게 코딩 초심자가 아닌 이상은 에러를 보기 쉽지 않고 최근의 ECMA6나 컴프레셔들은 완벽한 세미콜론 자동 삽입을 지원하므로 우리가 신경쓸 것이 거의 없다.
그렇다면 세미콜론 사용의 장/단점은 무엇일까?

세미콜론은 필요 없다?

  1. 코드량이 줄어든다
    세미콜론 또한 코드의 한 부분이므로, 붙이지 않아도 된다면 실질적인 코드량이 줄어든다.

  2. 일관성있다
    일관된 한 가지의 규칙을 가져야 한다면 세미콜론을 붙이지 않는 것이 낫다. 예를 들어 if나 for문, switch문을 쓸 때는 블록 뒤에 세미콜론을 붙이지 않는 것이 맞지만 습관적으로 닫히는 } 뒤에 일관적으로 세미콜론을 붙일 수 있는데, 헷갈리게 한다면 차라리 다 빼는 것이 맞다.

  3. 예쁘다
    단순히 예쁘고 깔끔하다는 이유 하나만으로도 세미콜론을 빼는 이유는 충분하다.
    사실 세미콜론을 쓰지 않는 사람들한테 물어보면 10의 6은 이 대답으로 시작한다. 개인적인 취향이기는 하나, 덕지덕지 붙은 세미콜론을 제거하는 것만으로도 심미적인 만족감이 드는 것은 개발자로서 어쩔 수 없다.

세미콜론은 강제되어야 한다?

  1. 안전성
    예시를 통해 자동 세미콜론 삽입기능이 에러를 뱉어낼 수 있는 경우를 몇 가지 보았는데, 이러한 오류가 발생할 여지가 있다면 그냥 처음부터 명시적으로 세미콜론을 사용하는 것이 좋다.
    예상할 수 있는 코드를 작성하는 것이 자동 파싱보다 더욱 안전한 것은 반박 불가능한 사실이다.

  2. 속도
    인터프리터가 코드를 해석할 때 세미콜론을 붙이지 않는 경우 조금 더 느리다는 비교가 있다.
    https://jsperf.com/asi-performance/2
    하지만 아주 미미하다.

  3. 발전에 따른 부합성
    TC39의 발표에 동의하는 것으로, 앞으로 언어가 발전함에 있어서 세미콜론을 사용해야 하는 경우는 더욱 더 늘어날 것이라는 주장이다. 자세한 것은 TC39의 주장을 읽어보는 것이 도움이 될 것이다.


무엇이 표준이 될지는 모르겠지만, 이미 많은 사람들이 자바스크립트의 유연성을 잘 이용하고 있다. 한번 들인 코딩 습관을 고치는 것은 퍼포먼스 측면에서나 노력 면에서나 아주 괴로운 일임에 틀림없다.
뭐 세미콜론을 강제하는 것이 표준이 된다 하더라도, linter나 pretter 등을 사용하면 문제되지 않을 부분이기도 하다. 그러나 이유를 알고 사용하는 것과 모르고 사용하는 것의 차이가 분명히 존재하므로 한 번씩 공부해 보면 좋은 주제일 것 같다.

나 같은 경우는 grunt로 여러 js파일을 minify하는 과정에서 예상했던 코드로 파싱되지 않아 에러를 본 일이 있고, 객체 선언 뒤에 세미콜론이 없다며 크로스브라우징이 되지 않았던 경험도 있었다 ( ie 구버전에서 에러가 났을 때 정확한 원인을 뱉어 주지 않는 경우가 있어 이틀간 삽질을 했었다 )
이러한 경우를 방지하기 위해서라도 명시적으로 세미콜론을 사용하는 습관을 들일 예정이다.
나같은 경우 툴 사용이 익숙하지 않아 그렇다 치지만 여러분은 linter에 익숙해져 있을 테니 조금 더 순조롭게 코딩 스타일을 지켜나갈 수 있을 것이라 생각한다.

자바스크립트 개발자로서 알아야 할 10가지 면접질문(1)

Posted on 2017-12-21 | Edited on 2019-01-06

1. 자바스크립트 앱 개발자에게 있어 중요한 두 가지 프로그래밍 패러다임에 대해 말해볼 수 있는가?

자바스크립트는 OOP(Object-Oriented Programming) 및 함수형 프로그래밍, 필수적/절차적 프로그래밍을 지원하는 다중 패러다임 언어이다.

자바스크립트는 프로토타입 상속으로서 OOP를 지원한다.

알아두면 좋은 것들:

  • 프로토타입 상속 (프로토타입, OLOO)
  • 함수형 프로그래밍 (클로저, 일급함수, 람다lambras)

주의할 점:

  • 패러다임이 무엇인지 모르거나, 프로토타입이나 함수형 프로그래밍에 대한 언급이 없으면 안 됨.

더 공부해볼 것:

  • The Two Pillars of JavaScript Part 1 — 프로토타입 OOP.
  • The Two Pillars of JavaScript Part 2 — 함수형 프로그래밍.

2. 함수형 프로그래밍이란 무엇인가?

함수형 프로그래밍은 수학적 함수를 조합하여 프로그래밍 하고, 데이터 상태의 공유나 변경을 피한다.

1958년에 나온 Lisp는 함수형 프로그래밍을 지원하는 최초의 언어 중 하나였으며, lambda 미적분에 큰 영향을 받았다. Lisp 과 Lisp 계열의 많은 언어들이 오늘날에도 보편적으로 사용되고 있다.
Lisp 위키

함수형 프로그래밍은 ‘자바스크립트의 두 가지 핵심적 요소’에서 필수적인 개념이다.
일반적으로 쓰이는 몇 가지 함수형 유틸이 es5 에 추가되었다.

알아두면 좋은 것들:

  • 순수함수(Pure function) / 함수 순수성
  • 사이드 이펙트 피하기
  • 함수는 심플하게 구성하기
  • 함수형 언어의 예 : Lisp, ML, Haskell, Erlang, Clojure, Elm, F Sharp, Ocaml, Scala 등등..
  • FP를 지원하는 기능에 대한 언급: 일급함수, 고차함수, argument/value로서의 함수

주의할 점:

  • 순수함수와 사이드 이펙트에 대한 언급이 없으면 안 됨.
  • 함수형 언어의 예시를 들 수 없으면 안 됨.
  • FP를 사용할 수 있는 자바스크립트의 형태를 모르면 안 됨.

더 공부해볼 것:

  • The Two Pillars of JavaScript Part 1 — 프로토타입 OOP.
  • Dao 의 불변성
  • 소프트웨어 작성법
  • 하스켈 뮤직 스쿨

3.고전적인 상속과 프로토타입 상속의 차이점은 무엇인가?

클래스 상속: 인스턴스(청사진처럼 클래스를 묘사하는 것)를 상속하고 계층 관계를 구축한다. 이는 계층적 클래스 분류이다.

인스턴스는 일반적으로 생성자 함수를 통해 new 로 인스턴스화된다. 클래스 상속은 es6의 ‘class’를 사용할 수도 안 할수도 있다.

프로토타입 상속: 인스턴스는 다른 객체로부터 직접 상속받게 된다. 인스턴스는 일반적으로 팩토리 함수 또는 ‘Object.create()’를 통해 인스턴스화 된다. 인스턴스는 다양한 객체로 구성될 수 있기 때문에 선택 상속을 쉽게 할 수 있다.

자바스크립트에서 프로토타입 상속은 클래스상속보다 간단하고 유연하다.

알아두면 좋은 것들:

  • 클래스: 밀접한 결합이나 계층 구조/분류를 만든다.
  • 프로토타입 : 체인 상속, 프로토타입 위임, 함수 상속, 객체 구성에 대한 것.

주의할 점:

  • 클래스 상속에 대한 프로토타입 상속과 컴포지션에 대한 선호도를 어필

4. 함수형 프로그래밍과 객체지향 프로그래밍의 장점과 단점은 무엇인가?

  • OOP의 장점 : 객체의 기본개념을 이해하기가 쉽고 메소드 콜의 의미를 해석하기가 쉽다. OOP는 선언형보다 명령형 스타일을 사용하는 경향이 있다. 이 형식은 컴퓨터가 따라야 할 방향과 똑같이 진행된다.

  • OOP의 단점 : OOP는 공유된 상태에 의존적이다. 객체와 행위는 일반적으로 동일한 엔티티에 고정되어 있으며, 비결정적(non-deterministic) 순서로 임의의 수의 함수에서 무작위로 액세스할 수 있으므로 바람직하지 않은 동작이 발생할 수 있다. 경쟁 조건(race condition)같은 예가 그것이다.

  • FP의 장점 : 함수형 패러다임을 이용해서, 상태 공유 또는 사이드 이펙트를 피할 수 있다. 이는 같은 리소스에 대해 경쟁하는 어러 함수 때문에 생길 수 있는 버그를 제거한다. point-free형(일명 암묵적 프로그래밍) 함수를 사용하면 OOP에 비해 함수를 훨씬 근본적으로 단순화하고 재구성 가능하게 만들 수 있어 재사용이 용이하다.

FP는 또한 작업의 단계별 명령을 맞추는 대신 선언형과 의미구조 스타일을 선호하고 ‘무엇을 할 것인가’에 집중하는 경향이 강해서, 해야할 일에 집중하여 기본적인 기능이 ‘어떻게 처리될 것인가’를 알려준다.
이는 리팩토링과 성능 최적화에 엄청난 도움이 되며 전체 알고리즘을 코드 변경이 거의 없는 상태에서 보다 효율적으로 대체할 수 있다. 메모이제이션이나 엄격평가(eager evaluation) 대신 지연평가를 사용하는 예를 들수 있다.

순수함수를 사용하는 계산의 경우 리소스 충돌이나 레이스 컨디션 등을 우려하지 않고도 다중 프로세서나 분산 컨퓨팅 클러스터 전반적으로 쉽게 확장 가능합니다.

FP의 단점 : Point-free 또는 대형 프로젝트 조합의 경우 FP의 과도한 사용은 결과코드가 더 추상적으로 지정되고, 간단하며 덜 구체적이기 때문에 가독성이 떨어지는 위험을 잠재적으로 가지고 있다.
많은 사람들이 함수형 프로그래밍보다는 OOP 및 명령형 프로그래밍에 익숙하기 때문에 함수형 프로그래밍의 일반적인 사용법조차 새로운 팀원에게는 혼란스럽게 받아들여질 수 있다.

FP는 OOP보다 학습 곡선이 가파르다. 왜냐하면 OOP의 광범위한 인기로 인해 OOP 언어나 학습자료는 훨씬 편안하게 공유될 수 있었지만, FP 언어는 훨씬 학문적이고 공식적인 경향이 있었다.
FP 개념은 lambda 미적분, 대수학 및 범주 이론의 관용구나 표기법을 어떻게 사용하는지에 대해 논한다. 이 모든 것이 사전 지식을 필요로 한다.

알아두면 좋은 것들:

  • 상태 공유와 관련된 문제에 대한 것들, 동일한 리소스에 대해 경쟁하는 다양한 것들, 기타 등등..
  • 학습 곡선의 차이에 대한 인식
  • 사이드 이펙트와 프로그램 유지 관리에 미치는 영향
  • 고도의 함수 코드베이스이기 때문에 학습곡선이 높음
  • 고도의 OOP 코드베이스가 동등한 FP 코드베이스와 비교했을 때 변화하기 쉽고 깨지기도 쉬움을 인식
  • 불변성은 접근이 매우 쉽고 유연한 상태 히스토리를 제공하여 무한한 undo/redo, rewind/replay, 시간 이동 디버깅 등의 기능을 쉽게 추가할 수 있다. 불변성은 어느 패러다임에서든 가능하지만 공유된 상태가 많은 객체가 확산되는 것은 OOP의 구현을 복잡하게 한다.

주의할 점:

  • 한 패러다임 또는 다른 패러다임의 단점을 열거할 수 없으면 안 된다. 어느 스타일로든 코딩해 본 경험이 있다면 제한사항 중 일부에 부딪혀 본 경험이 있을 것이다.

더 공부해볼 것:

  • The Two Pillars of JavaScript Part 1 — 프로토타입 OOP.
  • The Two Pillars of JavaScript Part 2 — 함수형 프로그래밍.

이는 https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95 를 번역한 것입니다.

자바스크립트 개발자로서 알아야 할 10가지 면접질문(2)

Posted on 2017-12-20 | Edited on 2019-01-06

5. 고전적인 상속을 선택하기 적절한 경우는?

답은, 적절한 경우는 거의 없다. 물론 싱글 레벨 이상에서 말고. 다중 레벨 클래스의 계층 구조는 안티패턴이다. 나는 이 도전을 몇 년간 이슈화 해 왔으며, 몇 가지 일반적인 오해 말고는 시원한 정답을 못 들었다.
종종 도전은 침묵을 마주해야 할 때가 있다.

함수는 때때로 유용하고 때로는 위험하며, 더 나은 선택이 있다면 늘 더 나은 선택을 사용하라: Douglas Crockford

알아두면 좋은 것들:

  • 아주 가끔 혹은, 거의 쓰지 마라. 혹은 아예 쓰지 마라.
  • 리액트 컴포넌트처럼 프레임워크 베이스에서의 클래스 같은 단일 레벨에서는 떄떄로 괜찮다.
  • 클래스 상속에 비해 오브젝트 구성을 선호

더 공부해볼 것:

  • The Two Pillars of JavaScript Part 1 — 프로토타입 OOP.
  • 자바스크립트 오브젝트 — 상속받은 객체.

6. 언제가 프로토타입 상속이 적절한 경우인가?

몇 가지 유형의 프로토타입 상속이 있다.

  • 위임 (즉, 프로토타입 체인)
  • Conceatenative (즉, mixin. ‘Object.assign()’)
  • Functional (함수형 프로그래밍과 혼동하지 말 것. 프라이빗 상태/ 캡슐화를 위한 클로저를 만드는 데 사용된다)

프로토타입 상속의 각 유형이 각각의 유스케이스를 가지고 있지만, 그것들은 모두 그와 동등한 유용한 조합 능력을 가지고 있다.
그것들은 has-a 나 uses-a, can-do 관계를 형성한다. 이는 클래스 상속으로 이루어지는 is-a 관계보다 더 유용할 수 있다.

알아두면 좋은 것들:

  • 모듈 또는 함수형 프로그래밍이 확실한 솔루션을 제공하지 않는 상황
  • 여러 소스에서 오브젝트를 생성해야 하는 경우
  • 당신이 상속을 필요로 하는 모든 상황

주의할 점:

  • 프로토 타입을 사용해야 하는 경우에 대한 지식이 없으면 안 된다.
  • mixin 이나 ‘Object.assign()’에 대한 개념이 없으면 안 된다

더 공부해볼 것:

  • 자바스크립트 어플리케이션 프로그래밍 — 프로토타입.

7. 클래스 상속보다 객체 구성을 선호한다는 것은 무엇을 의미하는가?

이것은 “Design Patterns: Elements of Reusable Object-Oriented Software”
에서 인용한 것이다. 즉 코드 재사용은 클래스에서 상속받고 객체 분류를 만드는 대신에 작은 단위의 함수를 new 객체로 모으는 방식으로 수행되는 것이 좋다.
즉, is-a 관계 대신 can-do, has-a 또는 uses-a 관례를 사용하라.

알아두면 좋은 것들:

  • 클래스 하이어라키 구조를 피하라.
  • 다루기 힘든 클래스 이슈는 피하라.
  • 엄격한 분류를 피하라 (is-a 관계를 강제하는 것은 새로운 유스케이스의 경우에도 결국 틀린 것이다)
  • 고릴라와 바나나 문제 (당신은 바나나를 원했지만, 당신이 얻은 것은 바나나를 들고 있는 고릴라와 전체 정글이었다)
  • 코드를 좀더 유연하게 짜라.

주의할 점:

  • 위의 문제를 언급하지 못하면 안 된다.
  • 컴포지션과 클래스 상속 또 컴포지션의 장점간의 차이점을 명확하게 설명하지 못하면 안 된다.

더 공부해볼 것:

8. 양방향 데이터 바인딩 및 단방향 데이터 흐름은 무엇이며, 무엇이 다른가?

양방향 데이터 바인딩은 UI 필드가 모델 데이터에 동적으로 바인딩되는 것을 의미하며, UI 필드가 변경되면 모델 데이터가 함께 변경되며 그 반대의 경우도 마찬가지다.

단방향 데이터는 모델이 단일 소스임을 의미한다. UI의 변경 사항은 사용자의 의도를 모델에 알리는 메시지를 트리거한다(또는 React에서의 store). 모델만이 앱의 상태를 변경할 수 있다. 데이터가 늘 한 방향으로 흐르기 때문에 결과를 이해하기 쉽다.

단방향 데이터 흐름이 결정론적인 반면, 양방향 바인딩은 사이드 이펙트를 일으켜 더 따라가기 힘들고 이해하기 어렵다.

알아두면 좋은 것들:

  • 리액트는 단방향 데이터 플로우의 새로운 표준사례이며, 리액트에 대한 언급을 하는 것은 아주 좋은 신호이다. Cycle.js는 단방향 데이터 흐름의 또 다른 대중적인 구현 방법이다.
  • Angular는 양방향 바인딩을 사용하는 인기있는 프레임워크이다.

주의할 점:

  • 둘중 어느 것을 의미하는지에 대한 이해가 있어야 한다. 그 차이를 명확하게 설명해야 한다.

더 공부해볼 것:

9. 모놀리식 vs 마이크로서비스식 설계의 장단점은 무엇인가?

모놀리식 아키텍처는 앱이 동일한 메모리 공간과 리소스를 공유하면서 함께 돌아가도록 설계된 코드 단위로 작성되었다는 것을 의미한다.
마이크로서비스 아키텍처는 앱이 자체적인 메모리 공간 안에서 실행되고 잠재적으로도 다양한 개별 시스템에서 독립적으로 확장될 수 있는 더 작고 독립적인 앱으로 구성되었다는 것을 의미한다.

모놀리식의 장점: 모놀리식 아키텍처의 가장 큰 장점은 일반적으로 로깅, 속도 제한, DOS 보호 같은 보안기능 등과 같은 많은 앱에서 크로스커팅 관심사(cross-cutting concern)가 많이 발생한다는 것이다.
모든 것이 동일한 앱을 통해 실행될 때, 크로스커팅 관심사에 구성 요소를 조합하기가 쉽다.
공유 메모리 액세스는 프로세스간 통신 (IPC) 보다 빠르기 때문에 성능상의 이점이 있다.

모놀리식의 단점: 모놀리식 앱은 앱이 발전함에 따라 밀접하게 결합되고 얽히게 되는 경향이 있다. 독립적인 확장이나 코드 유지보수를 위해 서비스를 분리하기가 어려워진다.
모놀리식 아키텍처는 특정 서비스나 콘트롤러를 볼 때 명확치 않은 종속성이나 사이드 이펙트가 있기 때문에 이해가 훨씬 어렵다.

마이크로서비스의 장점: 마이크로서비스 아키텍처는 보통 더 잘 구성되어 있다. 각 마이크로서비스는 각자 특정한 기능이 있으며 다른 구성요소의 작업에는 신경쓰지 않는다. 분리된 서비스는 다른 앱(예를 들어 웹 클라이언트와 퍼블릭 API를 둘 다 제공할 때)
의 목적을 충족시키기 위해 재구성하는 것이 더 원활해진다.

마이크로서비스의 단점: 새롭게 마이크로서비스 아키텍처를 구축할때 설계 시점에 예상치 못한 많은 문제를 발견하게 될 수 있다. 모놀리식 앱은 별 노력 없이도 이런 크로스 커팅 문제를 처리할 수 있는 share magic helper 또는 미들웨어를 구축할 수 있다.
결국, 모놀리식 아키텍처조차도 크로스커팅 이슈를 위해 외부 서비스 레이어를 통해 트래픽을 라우팅하는 경향이 있지만 단일 아키텍처를 사용하면 프로젝트가 훨씬 안정될 때까지 해당작업의 비용을 지연시킬 수 있다.
마이크로서비스는 자체 가상 머신이나 컨테이너에 자주 배포되므로 VM 논쟁이 확산되고 있다.

알아두면 좋은 것들:

  • 초기비용이 높은데도 불구하고 모놀리식 앱 대신 마이크로서비스에 대한 긍정적인 태도가 확산됨. 마이크로서비스는 장기적으로 성능이 향상되고 확장되는 경향이 있다.
  • 마이크로서비스와 모놀리식 앱에 대한 경험. 코드레벨에서 서비스가 서로 독립적이 되도록 앱을 구성해야 하지만 처음에는 모놀리식 앱이 함꼐 번들링하기 쉽다. 마이크로서비스의 간접비용은 비용이 실질적으로 들 때까지 지연될 수 있다.

주의할 점:

  • 모놀리식과 마이크로서비스 아키텍처의 차이를 모르면 안 된다.
  • 마이크로서비스의 추가 오버헤드에 대해 비현실적이거나 비실용적으로 인식하고 있으면 안 된다.
  • 마이크로서비스를 위한 IPC및 네트워크 통신으로 인한 추가 오버해드를 인식하지 못하면 안 된다.
  • 독립적으로 확장 가능한 마이크로서비스의 이점을 과소평가하면 안 된다.

10. 비동기식 프로그래밍이란 무엇이며, 자바스크립트에서 왜 중요한가?

동기식 프로그래밍이란 조건부나 함수 호출을 제외하고 코드가 위에서부터 순차적으로 실행되며 네트워크 요청이나 디스크 I/O 와 같이 장기적으로 실행되는 작업을 차단하는 것을 말한다.

비동기 프로그래밍이란 엔진이 이벤트 루프에서 실행됨을 의미한다. 차단이 필요할 때 요청이 시작되고 코드는 결과를 차단하지 않고 계속 실행된다. 응답이 준비되면 인터럽트가 발생하여 이벤트 처리기가 실행되고,
제어 플로우가 지속된다. 이런 식으로 단일 프로그램 스레드는 동시에 많은 작업을 처리할 수 있다.

사용자 인터페이스는 기본적으로 비동기식이며, 사용자 입력을 기다리는 대부분의 시간을 이벤트 루프를 중단하고 이벤트 핸들러를 트리거하는 데 소비한다.

노드는 기본적으로 비동기식이다. 즉 서버가 동일한 방식으로 작동하고, 네트워크 요청을 위한 루프에서 대기하며 첫 번째가 처리되는 동안 들어오는 요청을 더 받아들일 수 있다.

이것은 자바스크립트에서 중요하다. 비동기식 프로그래밍은 사용자 인터페이스 코드에 매우 자연스레 어울리며 서버의 성능에 매우 유용하기 때문이다.

알아두면 좋은 것들:

  • 블락킹의 의미와 성능에 미치는 영향에 대한 이해
  • 이벤트 처리에 대한 이해와 UI코드에 이것이 왜 중요한지에 대한 이해.

주의할 점:

  • 비동기식 또는 동기식이라는 용어에 익숙해야 한다.
  • 성능관련 이슈나 비동기 코드와 UI 코드간의 관계를 명확히 밝힐 수 없으면 안 된다.

이는 https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95 를 번역한 것입니다.

Awesome List

Posted on 2017-12-19 | Edited on 2019-01-06

github의 AWESOME LIST 소개

경쟁력 있는 코딩을 위한 최신 트렌드

https://github.com/topics/awesome-list

필요한 영역에 대한 최신 트렌드가 궁금하거나, 무료 pdf가 필요하다 싶으면 여기만 들어가보면 된다.

특히 프론트엔드 관련 자료는 한번에 정리되어 있는 곳을 찾기 참 힘든데, 이 곳만 들어가 보면 관련 레퍼런스나 최신 오픈소스들을 한번에 확인할 수 있다.

  • Frontend 관련 목록 : https://github.com/sindresorhus/awesome#front-end-development

  • 무료로 풀린 한글 프로그래밍 문서 : https://github.com/EbookFoundation/free-programming-books/blob/master/free-programming-books-ko.md

이 awesome list 를 또 정리해 놓은 awesome-awesomeness 도 있음.

https://github.com/bayandin/awesome-awesomeness

map 이해하기

Posted on 2017-11-19 | Edited on 2019-01-06

Map 이해하기

map 함수의 이해와 정의

map 메소드는 배열의 모든 요소들에 함수를 적용한 후 새로운 배열을 리턴한다.

예를 들어 보자

1
2
3
4
5
6

let newArray = oldArray.map((val, index, arr) => {

// 엘리먼트를 새로운 배열로 반환

})
  • newArray - 반환된 새로운 배열
  • oldArray - map 함수를 실행하는 배열
  • val - 처리되는 현재 값
  • index - 처리되는 값의 현재 인덱스
  • arr - 원래 배열

Map vs for문

map 함수를 for문과 같은 것으로 이해할 수도 있다. 값을 변환하기 위해서라는 점에 있어서는 그렇게 생각할 수도 있다.

다음 예제를 보자.

1
2
3
4
5
6
7
8
9
10
11
12

var arr = \[1, 2, 3, 4\]

var plus5 = \[\]

for(var i = 0 i < arr.length i++) {

plus5\[i\] = arr\[i\] + 5

}

// plus5 = \[6,7,8,9\]

이 코드는 이전 배열에 각각 5를 더하는 새로운 배열을 생성한다. 이 코드와 동일한 결과를 얻을 수 있는 훨씬 쉬운 방법이 map()이다.

이 코드를 map() 으로 변환하기 위해서는 단순한 배열을 하나 선언한다

1
2

let arr = \[1,2,3,4\]
  • arr - 우리가 매핑할 배열이다. 각 값에 5를 더한 값을 반환한다.
1
2
3
4
5
6

let plus5 = arr.map((val, i, arr) => {

return val + 5

})

이제 두 개의 배열을 로그에 출력해 보자. arr 배열은 변경되지 않은 채 plus5 배열이 새로운 값을 갖게 된다.

1
2
3
4

arr = \[1,2,3,4\]

plus5 = \[6,7,8,9\]

끝! 더 이상 배열에 직접 값을 더할 필요가 없어졌다. map() 함수를 이용하면, 설계한 것을 정의만 하면 된다. map() 함수가 나머지는 다 알아서 해준다.

Map 예제 2

이 예제에서는 value와 index 인자를 사용할 것이다.

지난번과 같은 기본적인 배열로 시작하자.

1
2

let arr = \[1,2,3,4\]

하지만 이번에는 객체를 반환할 것이다. 이 객체는 해당 인덱스에 인덱스와 값을 포함한다. 이 작업을 위해, 다음과 같은 i 인자를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
newArr = arr.map ((val, i, arr) => {

return {

value : val,

index : i

}

})

이렇게 하면 newArr을 통해 새로운 객체 배열이 생성된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
arr = \[1,2,3,4\]

newArr = \[

{value: 1, index: 0},

{value: 2, index: 1},

{value: 3, index: 2},

{value: 4, index: 3}

\]

** 다행히 map() 배열 내에서 반환해 주는 것은 새로운 배열을 만드는 데 사용한다. 현재 value, 현재 index 또는 전체 배열을 활용해서 반환할 요소를 정할 수 있다.
정적인 문자열을 반환하는 것도 할 수 있다(실제로 아무런 연결점도 없지만).

1
2
3
4
5
6
7
8
9
10

let arr = \[1,2,3,4\]

newArr = arr.map (() => {

return 'cats'

})

// newArr = \[ 'cats', 'cats', 'cats', 'cats'\]

Map 예제 3

지금까지의 모든 예제에서는 원래 배열의 모든 값을 변환했다. 만일 배열의 값들 중 일부만 변환하고 싶다면 어떻게 해야 할까?

이 문제를 해결할 수 있는 방법에는 여러가지가 있다. 가능한 해결책을 살펴보자.

이 예제에서도 단순한 배열을 선언해 보겠다.

1
let arr = \[1,2,3,4\]

짝수를 2배 곱하고 홀수는 그대로 두는 코드를 작성해 보자. 이를 위해 map() 함수 내에 로직을 이렇게 추가할 수 있다.

1
2
3
4
5
6

newArr = arr.map ((v, i, a) => {

return v % 2 === 0 ? v \* 2 : v

// newArr = \[1,4,3,8\]

끝이다. 짝수 값은 2배가 되고 홀수는 그대로 유지되었다.

위 코드가 이해가 되지 않는다면 다음과 같이 작성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
newArr = arr.map ((v, i, a) => { 

if (v % 2 === 0) {

return v \* 2

} else {

return v

}

})

// newArr = \[1,4,3,8\]

위와 똑같은 코드를 읽기 쉽개 조금 확장해 보았다.

먼저 인자를 2로 나는 값의 나머지가 0인지를 확인한다. 이것은 숫자가 짝수인지 홀수인지를 판별하는 가장 간단한 방법이다.
짝수는 항상 나머지 0을 가지고, 홀수는 항상 나머지 1을 가진다.

나머지가 0이면 숫자가 짝수임을 의미하므로, 이 케이스에서는 현재 값을 2배 해서 반환한다.
나머지가 1이면 숫자가 홀수임을 의미하므로, 이 케이스에서는 v값을 변경하지 않은 채 반환한다.

**원본: (https://codeburst.io/learn-understand-javascripts-map-function-ffc059264783)

12

Monica Kwon

12 posts
7 tags
© 2019 Monica Kwon
Powered by Hexo v3.8.0
|
Theme – NexT.Muse v6.7.0