https://medium.com/@pragyan88/writing-middleware-composition-and-currying-elegance-in-javascript-8b15c98a541b

 

Writing Middleware —Composition and Currying Elegance in JavaScript

Javascript is an elegant and beautiful language which allows the developers to have the flexibility to move between the world of imperative…

medium.com

 

Function Composition & Pipe

Pipeline ( 컴퓨터 과학에서 파이프라인은 한 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 형태로 연결된 구조를 가리킨다. )

function composition( 함수 작성? )은 한 함수의 결과를 다른 함수의 인풋으로 파이프라이닝하며 새로운 함수를 만드는 작업이다.

기본적으로 2개의 함수로 구성된다. A의 함수 결과는 B의 파라미터가 된다. 결국 B는 A에 따라 바뀌는 새로운 함수가 된다. 이러한 방식을 수학적으로 o 표시와 같다고 보면된다. f o g 이런거.

function composition을 코드로 보면 이렇다. 아래에는 2개의 함수를 받아 처리한다.

function compose(f, g) {
 return function(a) { 
  return f( g(a) );
 }
}

function add1(a){
 return a+1;
}

function multiply5(a){
 return a*5;
}

var add1Multiply5 = compose( add1,multiply5 );

console.log( add1Multiply5(6) ); //31

compose 함수의 인자 f, g는 둘 다 함수이다. 위의 코드를 보기 쉽게 하면 compose( add1(  multiply5( a ) ) ); 이런 함수가 된다. 

그러니까 compose 함수의 파라미터 값인 add1, multiply5 함수가 A함수이고 A함수의 리턴값이 곧 B함수인 compose의 파라미터 값이 된다.

그래서 새로운 함수인 add1multiply5 함수가 나왔다.  

function composition 보다 더 친숙하게 느껴질 수 있는게 있다. ( 유닉스에 쓰인다고한다. ) Pipe이다.

pipe는 function composition과 개념은 비슷하지만 함수 파라미터로 배열을 받아 새로구성된 함수를 만든다. 코드를 보자.

///////////////////////////////// A ///////////////////////////////////
const pipe = function(fns) {
 return function(item) {
  return fns.reduce( 
    function(prev, fn){
      return fn(prev);
    }, item );
 }
}

///////////////////////////////// B ///////////////////////////////////
const pipe = function([add1, multiply5]) {
	return function(item) {
    	return [add1, multiply5].reduce( prev, fn ) {
        	return fn( prev );  ------>  multiply5( add1( a ) );
        }, item );
    }
  }
  

function add1(a) {
 return a + 1;
}

function multiply5(a) {
 return a * 5;
}

var add1multiply5 = pipe( [add1, multiply5] );

console.log( add1multiply5(6) ); //35

 

pipe 변수에 함수배열인 fns를 받는 익명함수를 만들었다. 

위의 A라인을 풀어보면 B라인이 된다. 근데 보면 인자 item을 받은 곳이 없고 a를 집어 넣은 곳이 없다는 것을 알게 된다. 여기서 Currying 개념이 들어간다.

 

Currying

만약 수학적 함수가 하나의 파라미터만 받는다 해도, 만들어진 함수 그 이상의 파라미터를 받을 수는 없을까 ?. 커링의 개념은 이 생각의 자연스러운 확장이다. 

커링은 여러개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법을 말한다. 

function curriedAdd(a){
   return function(b){
      return a+b;
   }
}
curriedAdd(1)(2); //3

--------------------------------------

var add = curriedAdd(1);

add(2); // 3

위에서 인자 a를 받는 curriedAdd함수는 인자 b를 받고 a와 b를 더하는 익명함수를 리턴한다. curriedAdd는 a를 받고 b를 받는 걸 기다린다. 

위에처럼 쓰기도 하고 밑에 처럼 쓰기도 한다. 커링은 함수의 다양한 타입을 만드는 유연성을 갖고 있다. 

커링은 인자의 순서가 매우 중요하다. 앞에 있는 인자일 수록 변동가능성이 적지만, 뒤에로 갈 수록 변동 가능성이 높아 인자 순서를 고려해서 설계 해야한다.

 

Prototype Design Pattern 방식은 보통 퍼포먼스가 중요한 상황에서 사용되는 방법이다. 

보통 기존 객체를 복사하여( 상속 느낌 ) 재사용 하는 방식인데 예를들어 방대한 데이터베이스를 통해 만들어진 객체를 프로젝트의 다른 부분에서 쓰여야 할 때 이 패턴이 쓰인다. 복잡한 DB작업을 중복하여 하지 않고 해당 객체를 복사하여(상속하여 ) 사용한다.

물론, 객체를 다른 곳에서 사용하기 위해 생성자가 존재 해야 한다. 

재사용 한 코드는 대충 이렇다.

var TeslaModelS = function() {
  this.numWheels    = 4;
  this.manufacturer = 'Tesla';
  this.make         = 'Model S';
}


////////////////////////////// 비동기적 프로토타입 선언

TeslaModelS.prototype.go = function() {
  // Rotate wheels
}

TeslaModelS.prototype.stop = function() {
  // Apply brake pads
}

////////////////////////////// 동기적 프로토타입 선언

TeslaModelS.prototype = {
  go: function() {
    // Rotate wheels
  },
  stop: function() {
    // Apply brake pads
  }
}

 

프로토타입 패턴 방식도 코드를 캡슐화 시켜 public과 private 부분을 분할 시킬 수 있다.

var TeslaModelS = function() {
  this.numWheels    = 4;
  this.manufacturer = 'Tesla';
  this.make         = 'Model S';
}

TeslaModelS.prototype = function() {

  var go = function() {
    // Rotate wheels
  };

  var stop = function() {
    // Apply brake pads
  };

  return {
    pressBrakePedal: stop,
    pressGasPedal: go
  }

}();

https://scotch.io/bar-talk/4-javascript-design-patterns-you-should-know#undefined

 

4 JavaScript Design Patterns You Should Know

The Scotchmas Day 2 giveaway can be found at the end of this article. Every developer strives to write maintainable, readable, and reusable code. Code structuring becomes more important as applications become larger. Design patterns prove cr

scotch.io

 

JS 모듈은 흔히 쓰이는 디자인 패턴이다. 보통 독립적인 코드를 작성하기 위해 사용된다. 

OOP( Object-Oriented Programming )에 잘 어울리며 코드는 캡슐화 되어 JS class 단위로 운영된다. 여기에 특징 중 하나는 public과 private 구역을 나누어 사용할 수 있다.

모듈은 private 범위에 접근하기 위해 IIFE( Immediately-Invoked-Function-Expressions )으로 만들어진다. 

var HTMLChanger = (function() {
  var contents = 'contents'

  var changeHTML = function() {
    var element = document.getElementById('attribute-to-change');
    element.innerHTML = contents;
  }

  return {
    callChangeHTML: function() {
      changeHTML();
      console.log(contents);
    }
  };

})();

HTMLChanger.callChangeHTML();       // Outputs: 'contents'
console.log(HTMLChanger.contents);  // undefined

위에 코드 처럼 생겼다.

위에서 callChangeHTML 함수는 return 객체와 묶여있어 변수 contents를 불러낼 수 있지만 직접 contents 변수를 불러낼 수는 없다. 그래서 보통 return 에 묶어 노출 할( public 할 ) 변수를 결정한다. 

 

 

http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript/

 

Data structures in JavaScript

 

blog.benoitvallon.com

 

데이터 구조란, 컴퓨터공학에서 컴퓨터의 데이터를 구성하여 효율적으로 사용하는 특정한 방법이라고 위키가 설명했다. 위키 짱

 

모든 데이터 구조는 저마다 장점과 단점을 갖고 있다. 복잡성( Complexity, 복잡성 맞나 모르겠다.)은 특정한 문제에 따라 장점과 단점을 표현하는 방법이다. 복잡성에는 2가지로 표현된다. 공간과 시간.

공간 복잡성은 데이터 구조의 메모리 소비라고 보면 된다. 일반적으로 시간을 공간과 바꾸거나 공간을 시간과 바꾼다. 시간 복잡성은 공간 복잡성보다 더 다양하다. ( 뭔 말인지 모르겠다. )

 

The Array data structure

배열 데이터 구조는 최소 1개 이상의 값이나 변수의 집합으로 구성되어 있다. 그리고 데이터 구조의 가장 간단한 타입인 1차원 배열이자 직선으로 된 배열이다. // From Wikipedia

Complexity / Average   

Access Search Insertion Deletion
O(1) O(n) O(1) O(n)

 

배열 데이터 구조 사용 코드

// 빈 배열 만드는 함수 선언
function MyArray() {
  this.array = [];
}
// 만든 함수안의 배열에 데이터 추가하는 메서드 만듬 
MyArray.prototype.add = function(data) {
  this.array.push(data);
};
// 배열에 값을 제거하는 메서드 만듬
MyArray.prototype.remove = function(data) {
  this.array = this.array.filter(function(current) {
    return current !== data;
  });
};
// 배열안에서 값을 찾는 메서드 만듬
MyArray.prototype.search = function(data) {
  var foundIndex = this.array.indexOf(data);
  if(~foundIndex) {
    return foundIndex;
  }
  return null;
};
// 배열에서 값의 순서를 알아내는 메서드 만듬
MyArray.prototype.getAtIndex = function(index) {
  return this.array[index];
};
// 배열의 길이를 뱉는 메서드 만듬
MyArray.prototype.length = function() {
  return this.array.length;
};
// 배열의 값들을 콘솔에 찍는 메서드 만듬
MyArray.prototype.print = function() {
  console.log(this.array.join(' '));
};


var array = new MyArray();
array.add(1);
array.add(2);
array.add(3);
array.add(4);
array.print(); // => 1 2 3 4
console.log('search 3 gives index 2:', array.search(3)); // => 2
console.log('getAtIndex 2 gives 3:', array.getAtIndex(2)); // => 3
console.log('length is 4:', array.length()); // => 4
array.remove(3);
array.print(); // => 1 2 4
array.add(5);
array.add(5);
array.print(); // => 1 2 4 5 5
array.remove(5);
array.print(); // => 1 2 4

 

The Hash Table data structure

Hash Table ( Hash Map )은 연결된 배열을 실행하기 위한 데이터 구조, 그리고 배열의 값에 키를 맵핑 할 수 있는 데이터 구조이다. 

 

Complexity / Average   

Access Search Insertion Deletion
- O(1) O(1) O(1)

 

해시 테이블 데이터 구조 코드

function HashTable(size) {
  this.values = {};
  this.numberOfValues = 0;
  this.size = size;
}

HashTable.prototype.add = function(key, value) {
  var hash = this.calculateHash(key);
  if(!this.values.hasOwnProperty(hash)) {
    this.values[hash] = {};
  }
  if(!this.values[hash].hasOwnProperty(key)) {
    this.numberOfValues++;
  }
  this.values[hash][key] = value;
};

HashTable.prototype.remove = function(key) {
  var hash = this.calculateHash(key);
  if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) {
    delete this.values[hash][key];
    this.numberOfValues--;
  }
};

HashTable.prototype.calculateHash = function(key) {
  return key.toString().length % this.size;
};

HashTable.prototype.search = function(key) {
  var hash = this.calculateHash(key);
  if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) {
    return this.values[hash][key];
  } else {
    return null;
  }
};

HashTable.prototype.length = function() {
  return this.numberOfValues;
};

HashTable.prototype.print = function() {
  var string = '';
  for(var value in this.values) {
    for(var key in this.values[value]) {
      string += this.values[value][key] + ' ';
    }
  }
  console.log(string.trim());
};

var hashTable = new HashTable(3);
hashTable.add('first', 1);
hashTable.add('second', 2);
hashTable.add('third', 3);
hashTable.add('fourth', 4);
hashTable.add('fifth', 5);
hashTable.print(); // => 2 4 1 3 5
console.log('length gives 5:', hashTable.length()); // => 5
console.log('search second gives 2:', hashTable.search('second')); // => 2
hashTable.remove('fourth');
hashTable.remove('first');
hashTable.print(); // => 2 3 5
console.log('length gives 3:', hashTable.length()); // => 3

 

-- 데이터 구조 계속 추가 --

 

-- 27 데이터구조, 29 알고리즘 JS-33 블로그 번역 포기 다른거 찾아서 따로 올리겠음. 내 기준 자료가 너무 난잡함 --

async와 await는 동기적 코드처럼 보이는 비동기 코드를 짤 수 있게 한다.

https://flaviocopes.com/javascript-async-await/

 

Modern Asynchronous JavaScript with Async and Await

Discover the modern approach to asynchronous functions in JavaScript. JavaScript evolved in a very short time from callbacks to Promises, and since ES2017 asynchronous JavaScript is even simpler with the async/await syntax

flaviocopes.com

 

JS는 짧은 기간안에 콜백으로 부터 프로미스로 진화하는 기염을 토했다. 그리고 다시 한번 async/await로 심플 해졌다. 

async 함수는 프로미스와 제너레이터의 합작이다. 그리고 프로미스보다 더 추상적인 함수이다. 즉 async/await는 프로미스를 기초로한다.

 

일단  async/await가 탄생하게 된 이유는 프로미스의 보일러플레이트를 줄이고 프로미스 체인의 한계 때문에 나왔다.

프로미스는 콜백 지옥에서 빠져나오게 만들어 주었지만 사용하는 과정이 복잡했다. 이거 때문에 ES2017에서 async/await를 내놓았다.

 

그럼 이제 코드를 보자

// 프로미스 함수를 만든다. 3초 뒤에 'I did something'를 리턴한다.
const doSomethingAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('I did something'), 3000)
  })
}

// async로 함수를 만들고 콘솔을 찍는다.
const doSomething = async () => {
  console.log(await doSomethingAsync())
}

console.log('before');
// 실행
doSomething();

console.log('after');

// 콘솔 로그 결과
// before
// after
// I did something

함수 앞에 async 키워드를 넣으면 그게 async 함수이다. async함수는 내부적으로 프로미스를 만들어 프로미스를 리턴한다.

함수 앞에 await 키워드를 붙이면 프로미스 상태 값이 이행, 실패 되면 함수를 호출하게 한다.

 

A ====================================
const aFunction = async () => {
  return 'test'
}

B ====================================
const aFunction = async () => {
  return Promise.resolve('test')
}

A, B 라인의 코드는 같은 코드이다.

 

프로미스로만 만든 함수와 비교해보자. 두개 다 같은 코드이다.

일단 fetch도 프로미스를 리턴한다. ( fetch('주소', 설정객체).then(콜백).catch(콜백); 이런 식으로 사용한다. ajax를 더 쉽게 쓰는 api  )

== A =============================================================
const getFirstUserData = () => {
  return fetch('/users.json') // get users list
    .then(response => response.json()) // parse JSON
    .then(users => users[0]) // pick first user
    .then(user => fetch(`/users/${user.name}`)) // get user data
    .then(userResponse => userResponse.json()) // parse JSON
}

getFirstUserData()

== B =============================================================
const getFirstUserData = async () => {
  const response = await fetch('/users.json') // get users list
  const users = await response.json() // parse JSON
  const user = users[0] // pick first user
  const userResponse = await fetch(`/users/${user.name}`) // get user data
  const userData = await userResponse.json() // parse JSON
  return userData
}

getFirstUserData()

 

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261

 

Master the JavaScript Interview: What is a Promise?

“Master the JavaScript Interview” is a series of posts designed to prepare candidates for common questions they are likely to encounter…

medium.com

 

프로미스는 미래의 어떤 시점에 값을 만들어내는( or 오류가 나는 ) 객체이다. ( A promise is an object that may produce a single value some time in the future ). 자바스크립트에서 함수는 객체이다.

JS에서는 콜백함수 사용이 아주 빈번하다. 즉 비동기로 처리하는 작업이 많다는 얘기이다. 그런데 이러한 작업이 점차 복잡해지면서 우리는 콜백 지옥에 빠질 염려를 해야한다. 이때 복잡한 와중에 콜백지옥을 벗어나기 위해 우리는 프로미스를 사용한다. ( 비동기 처리 난무하는 코드 한 가운데서 그나마 동기적으로 작동 했으면 좋겠다는 코드를 동기처리 하게 만들어준다. 이거시 약속이다. )

 

프로미스는 동기적 함수로부터 비동기적 리턴이 될 수 있는 객체이다. 프로미스는 3가지 상태 중 하나를 취한다.

- Fulfilled / 이행했음

- Rejected / 실패했음

- Pending / Fulfilled도 아니고 Rejected도 아닌 상태

pending상태가 아니라면 프로미스는 정리되었다( be settled )라고 본다. 이걸 resolved라고 하기도 한다.

한번 정리된 프로미스는 다시 재정리( be resettled )되지 못 한다. resolve()나 reject()를 다시 호출해도 아무런 영향을 주지 못 한다. 정리된 프로미스의 불변성은 프로미스의 중요한 특징이다.

네이티브 JS는 프로미스 상태를 노출하지 못 한다. 대신 프로미스를 만드는 함수는 프로미스의 상태에 대해 알고있고, resolve나 reject에 접근 함으로써 알 수 있다.

 

아래 코드는 특정 시간 이후 resolve하는 프로미스를 리턴하는 함수이다. 

const wait = time => new Promise( (resolve) => setTimeout(resolve, time) );

wait(3000).then(() => console.log('Hello!')); // 'Hello!'

 

ES6에서 프로미스 생성사는 함수를 취한다. 그리고 그 함수는 2개의 파라미터를 받는데 resolve()와 reject()다. 

위의 예제에서는 resolve()만 사용 하였다. 

보통 then에 있는 콜백 함수가 보낸 값과 함께 resolve()와 rejcect()를 마음대로 사용한다. 

 

프로미스는 특정 룰을 따른다.

- 프로미스는 .then() 메서드를 제공하는 객체이다.

- pending 상태는 fullfilled나 rejected 상태로 변한다.

- fulfilled나 rejected 프로미스가 정해지면 다른 상태로 변하지 않는다.

- 프로미스가 정해지면 이건 무조건 값을 가진다 그 값이 undefined 일지라도 그렇다. 값은 절대 변하지 않는다.

 

모든 프로미스는 then 메서드를 제공한다. 

// then 메서드 특징
promise.then(
  onFulfilled?: Function,
  onRejected?: Function
) => Promise

 

then도 룰이 있다,

- onFulfilled과 onRejected는 옵션이다.

- 받은 아규먼트가 함수가 아니라면 무시된다.

- onFulfilled는 프로미스가 fulfilled이면 호출된다. 

- onRejected는 프로미스가 rejected되면 첫 번째 인자로서 거절 사유와 함께 호출된다. 거절 사유는 아무 유효한 JS 값이 될 수 있다.

- onFulfilled와 onRejected는 한 번이상 호출되지 않는다.

- then은 하나의 프로미스에서 여러번 호출될 수 있다. 

- then은 새 프로미스를 리턴하게된다.

- 만약에 onFulfilled와 onRejected가 값 x를 리턴한다면, x는 프로미스이다. 

- 만약  onFulfilled와 onRejected가 에러를 던진다면, 프로미스2는 그 에러를 이유로 rejected 한다.

 

then이 항상 새로운 프로미스를 리턴하기 때문에 프로미스를 계속 엮는게 가능하다. 

fetch(url)
  .then(process)
  .then(save)
  .catch(handleErrors)
;

 위 코드처럼 사슬 형식으로 엮는다.

위에서 fetch, process, save가 프로미스를 리턴하는 함수라면 process는 fetch가 끝날 때 까지 기다리다 시작한다. save도 process가 끝날 때 까지 기다리다 실행한다. catch()는 위의 프로미스들이 reject되면 실행한다.

 

아래 프로미스의 좋은 예시가 있어서 퍼왔다.

function getData() {
  return new Promise(function (resolve, reject) {
    var data = 100; // 이게 불러오기로 한 특정 데이터라고 치자
    resolve(data); // data가 잘 불러와지면 이거 ( 100이 잘 못될 일이 없으니까 이거 씀 )
    reject(data);  // data가 잘 안 불러와지면 이거
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData()
	.then(function (resolvedData) {
      console.log(resolvedData); // 100 -> 데이터 잘 들어왔다 치면 이게 로그찍힘
    })
    .catch( function (err) {
       console.log(err);  // 뭔가 오류 나면 이게 로그 찍힘
    });

출처 // https://joshua1988.github.io/web-development/javascript/promise-for-beginners/

 

참고한 블로그를 보면 에러 핸들링 하는 과정에서 then 안에 에러 핸들링 하는 메소드가 있다. 간단히 말하자면 그냥 catch()로 에러 잡아라.

 

I recommend ending all promise chains with a .catch().

 

https://www.sitepoint.com/es6-collections-map-set-weakmap-weakset/

 

ES6 Collections: Using Map, Set, WeakMap, WeakSet — SitePoint

MSDN's Kyle Pennell introduces the new ES6 collections Map, Set, WeakMap, WeakSet, explaining how and when to use these new data collection types.

www.sitepoint.com

 

- Collections

대부분의 프로그램 언어들은 데이터 콜렉션 타입을 갖고 있다.

파이썬의 경우 lists, utples, dictionaries 가 있고 자바는 lists, sets, maps, queues 그리고 루비는 hashes, arrays가 있다.

JS는 array만 있었는데 ES6 문법이 추가되면서 4가지 데이터 콜렉션 타입이 추가 되었다. 

Map, Set WeakSet, WeakMap 이거 4가지이다. ES6를 지원하는 엔진에서만 돌아간다.

 

ES6 이전에 주로 키/벨류 쌍으로 저장하는 방법으로 Object를 사용하였는데 몇 가지 불리한 점이 있었다.

1. 키 값이 String으로 되어있어야 했다. 다른 타입으로 해도 돌아는 갔지만 내부적으로 다른 타입을 String으로 바꿨기 때문에 쓸데없는 작업을 해야했다.

2.  객체는 콜렉션으로 사용하려고 만든게 아니다. 반복하는 부분을 코드로 작성 해줘야 한다. 반복 할 때 각 프로퍼티에 딸린 프로토타입 프로퍼티도 같이 왔다 갔다 한다.

3. 객체 내부 메소드(constructor, toString, valueOf...)를 의식적으로 받아오지 않게 해야했다. 

 

암튼 객체는 콜렉션이 아니었고 다른 언어의 콜렉션처럼 사용하는데 몇가지 제약이 있었다. 이걸 해결하기 위해 ES6에서 콜렉션 메서드들을 추가했다.

map을 만들고 사용하는 예제/

// 새로운 맵을 만든다.
const map = new Map();
// 맵에 set메서드로 키/값 쌍으로 넣는다. 앞에게 키 뒤에게 값
map.set('hobby', 'cycling'); 

// 새로운 객체 생성
const foods = { dinner: 'Curry', lunch: 'Sandwich', breakfast: 'Eggs' }; 
// 또 다른 새로운 빈 객체 생성
const normalfoods = {}; 

// 맵에 위의 2개 객체를 키/값으로 넣는다.
map.set(normalfoods, foods); // Sets two objects as key value pair

// for..of 반복문으로 map콜렉션의 키,값 을 반복시킨다.
for (const [key, value] of map) {
  console.log(`${key} = ${value}`); // hobby = cycling  [object Object] = [object Object]
}

// forEach 반복문으로 map콜렉션의 키, 값을 반복시킨다.
map.forEach((value, key) => {
  console.log(`${key} = ${value}`); // hobby = cycling  [object Object] = [object Object]
}, map); 

// 맵에 있는 모든 키, 값을 지워버린다.
map.clear(); // Clears key value pairs
// 맵 사이즈 확인
console.log(map.size === 0); // True



*** 나중에 따로 정리 할거임 ***
foreach반복문 : 오직 Array 객체에서만 사용가능한 메서드( 반복문이 아니라 메서드임 )
for in 반복문 : 객체의 모든 열거 가능한 속성에 대해 반복
for of 반복문 : [Symbol.iterator] 속성을 가지는 컬렉션 전용 반복문

 

 

Set 콜렉션을 만들고 사용하는 예제

( map에 키/값을 집어넣을 때 set()을 사용하는데 여기서는 Set콜렉션이다. 다르다. 둘이. 뭐 이따구인지 모르겠다)

// 셋 컬렉션 생성
const planetsOrderFromSun = new Set();
// 셋 컬렉션에 값 집어넣음
planetsOrderFromSun.add('Mercury');
// 연속 넣기
planetsOrderFromSun.add('Venus').add('Earth').add('Mars'); 
// 컬렉션이 해당 값을 갖고 있는지 확인
console.log(planetsOrderFromSun.has('Earth')); // True

// 컬렉션에 해당 값 삭제
planetsOrderFromSun.delete('Mars');
// 삭제 확인
console.log(planetsOrderFromSun.has('Mars')); // False

// for of 로 반복돌림
for (const x of planetsOrderFromSun) {
  console.log(x); // 넣은 순서대로 나옴 Mercury Venus Earth
}
// 사이즈 확인
console.log(planetsOrderFromSun.size); // 3

// 중복 값 넣어보기
planetsOrderFromSun.add('Venus'); // Trying to add a duplicate
// 사이즈 확인 -> 중복 값 안들어감
console.log(planetsOrderFromSun.size); // Still 3, Did not add the duplicate

// 콜렉션 비우기
planetsOrderFromSun.clear();
console.log(planetsOrderFromSun.size); // 0

 

Weak Collection, Memory, 가비지 컬렉션

가비지 콜렉션은 메모리 관리의 한 형태이고 객체가 더이상 어딘가에 참조되지 않을 때 자동적으로 삭제한다.

Map, Set 컬렉션의 경우 객체로의 참조가 강하게 일어나는 컬렉션이어서 가비지 컬렉션의 대상이 안된다. 예를 들어 더이상 필요하지 않은 돔 엘리먼트 값을 계속 갖고 있는 경우가 있겠다.

ES6에선 이걸 해결하기 위해 WeakMap과 WeakSet을 사용하는데 객체가 쓸모 없어지면 메모리에서 지워버리게 만든다.

 

 

https://codeburst.io/understanding-generators-in-es6-javascript-with-examples-6728834016d5

 

Understanding Generators in ES6 JavaScript with Examples

ES6 introduced a new way of working with functions and iterators in the form of Generators (or generator functions). A generator is a…

codeburst.io

 

ES6에서 함수를 반복문과 함께 사용하는 방법을 추가 했는데 이게 Generators( 함수)다. 반복을 하다 중간에 멈출 수 있고 멈춘 시점 부터 다시 반복할 수 있다. ( async/await 가 이 제너레이터 기반이다. )

제너레이터란 무엇인가 ?

일반 함수는 작업을 끝낼 때 까지 멈출 수 없다. 끝내려면 return 해야한다. 아님 에러를 던지던가

제너레이터는 next()를 호출하는 객체를 리턴하는 함수이다. 모든 next() 호출은 { value: anything, done: true | false } 형태의 객체를 리턴한다.

객체 안의 done이 true가 되면 제너레이터는 멈춘다.  

제너레이터 동작 그림

위에 사진이 제너레이터가 동작하는 방식이다. 설명은 블로그에 안나와 있어서 나도 못한다.

제너레이터 문법을 보자

function * generator () {}
function* generator () {}
function *generator () {}

let generator = function * () {}
let generator = function* () {}
let generator = function *() {}

let generator = *() => {} // SyntaxError
let generator = ()* => {} // SyntaxError
let generator = (*) => {} // SyntaxError

제너레이터 문법은 function 키워드 바로 뒤에 * 을 붙이면 된다. 화살표 함수와는 사용 못 한다.

 

제너레이터 함수 안에는 yield라는 키워드가 있는데 이게 return과 비슷한 역할을 한다. 

function * withYield(a) {
  let b = 5;
  yield a + b;  // 1번 일드
  b = 6; // it will be re-assigned after first execution
  yield a * b;  // 2번 일드
}

const calcSix = withYield(6);

calcSix.next().value; // 11  -> 1번 일드 리턴됨
calcSix.next().value; // 36  -> 1번 일드 넘기고 2번 일드 리턴됨

yield로 내보낼 값을 정한다. 오직 한번만 리턴한다. 함수를 다시 호출해도 리턴했던 yield 다음 yield가 리턴된다.

불러낼 때 .next().value 로 불러온다. 일반적으로 제너레이터의 아웃풋은 항상 객체이다. 이 객체는 2가지 프로퍼티를 갖고 있는데 하나는 value 값이고 다른 하나는 done이다. 바로 이 done의 값이 true에서 false로 바뀌면 위의 코드처럼 일드를 넘기는거다.

일드처럼 return도 똑같이 객체를 뱉어낸다. 근데 yield가 아니라 return이 값을 뱉어내면 그 제너레이터 함수는 끝난다. 다음 라인에 yield가 있어도 읽지 않는다.

 

http://www.jstips.co/en/javascript/recursion-iteration-and-tail-calls-in-js/

 

Recursion, iteration and tail calls in JS

If you’ve been on the business for some time, you have, most likely, come across the definition of recursion, for which the factorial of a given number n! = ...

www.jstips.co

 

재귀 ( Recursion, factorial function )

function factorial(n) {
    if (n === 0) {
        return 1;
    }
    return n * factorial(n - 1);
}

예제에서 n이 6일 때 아래처럼 동작한다.

factorial(6)
  6 * factorial(5)
    5 * factorial (4)
      4 * factorial(3)
        3 * factorial(2)
          2 * factorial(1)
            1 * factorial(0)
            	1
            (resuming previous execution) 1 * 1 = 1
          (resuming…) 2 * 1 = 2
        (…) 3 * 2 = 6
      … 4 * 6 = 24
    5 * 24 = 120
  6 * 120 = 720
factorial(6) = 720

 

재귀 함수는 일단 끝까지 다 스텍에 쌓아놓고 안 쪽 부터 리턴된 값고 함께 스텍을 풀어나간다.

재귀를 빠져나올 조건을 잘 걸어줘야 한다. 안 그러면 스텍 사이즈오버 에러 나온다.

+ Recent posts