ジャバスクリプト、文字列操作(JAVASCRIPT & STRING)

Javascript文字列のメソッド : slice(), substring(), includes(), startsWith(), trim(), replace()…….

フロントエンド(React, Vue, Angular)もバックエンド(Nodejs)もジャバスクリプト(Javascript)が使われる時代になって、だんだんジャバスクリプトの文法が重要となりますね。

その中、文字列(String)関連のメソッドはよく使うのに、あまり覚えてはいないやつですね。?

文字列関連のメソッドは一度覚えておくと自動に覚えられるぐらい使う頻度が高いので最初だけ頑張ればですね!

では、頻繁に使うメソッドの中slice(), substring(), includes(), startsWith(), trim(), replace(), indexOf(), test()をご紹介します。


sliceメソッドはインデックスをパラメータで渡し、必要な文字列を抽出します。

スタートインデックス(start index)と終了インデックス(end index、省略可)を渡し、終了インデックスを省略する場合は文字列の最後までになります。

パラメータが0の場合は頭から一番目、-1は逆で後ろから一番目の意味になります。

const myWord = 'primavera';

myWord.slice(0,5);   // 'prima'
myWord.slice(-4);    // 'vera'
myWord.slice(3);     // 'mavera'

myWord.slice(3,1);    // '' -> slice()はスタートが終了より大きいと空を返す

substringメソッドもsliceと同じくインデックスを渡し、文字列を抽出します。

基本的な操作は同様ですがsubstringはスタートが終了より大きいの場合自動に位置を交換してメソッドを実施します。

また、substringに負数を入れると0と認識します。

例えば(3, -1)を渡すと(3, 0)として認識します。それに(3, 0)はスタートが大きいので自動交換し、(0, 3)を実施します。

メソッド名が似たようなsubstrは’位置’と’文字数’をパラメータに渡すメソッドです。

const myWord = 'invierno';

myWord.substring(3);     // 'ierno'
myWord.substring(1,3);   // 'nv'

myWord.substring(3,1);   // 'nv' -> 自動にスタートと終了位置交換

myWord.substring(3,-1);  // 'inv' -> 負数は0, 自動位置交換

myWord.substring(-4);    // 'invierno' -> 負数は0, スタート(0)から最後まで

myWord.substr(1,3);      // 'nvi' -> スタート1から三つ

includesは文字列の中、指定した文字列が含まれているかをチェックします。

パラメータは文字列(必須)とインデックス(省略可)を渡し、返すのはtrue或はfalseです。

このメソッドは指定文字はもちろん、配列の中で空の値を確認するためには使えます。

const myWord = 'alegria';

myWord.includes('le');       // true
myWord.includes('i');        // true
myWord.includes('rio');      // false

const myArray = ['dream','','king'];
myArray.includes('');       // true   -> myArray[1]が空なのでtrue
myArray.includes('dream');  // true   -> myArray[0]と一致
myArray.includes('drea');   // false  -> 要素は完全一致する場合のみtrue

startsWithメソッドも文字列が含まれているかをチェックします。

文字列のみ渡すと0インデックスから始まる指定文字の存在を確認し、BOOLEANを返します。

endsWithは指定文字で終わることをチェックします。

const mySentence = 'cafe con leche';

mySentence.startsWith('cafe');     // true
mySentence.startsWith('fe');       // false
mySentence.startsWith('fe',2);     // true

mySentence.endsWith('eche');      // true

trimは文字列両断の空白を除くメソッドです。

文字列変換の過程で入られた空白またはタイプミスで入った文字列は文字比較の時falseを返し、エラーになりがちです。

なのでtrimで空白を事前に除き、エラーを予防することができます。

文字列両断の空白、タップ、改行(/n)文字を除くことができますが、両断ではなく文字列中に入っているのは作動しません。

const mySentence = '  la casa de papel   ';

mySentence.trim();        // 'la casa de papel'

const mySentence = ' el amor \n';

mySentence.trim();       // 'el amor'

replaceは文字列から指定した文字を探して変換文字に変換します。

文字列の中に指定文字が一つ以上存在するとき、最初に見当たるのだけが変更対象になります。

例えば ‘my_name_is_Ron’からアンダーバーを全部空白にするためreplace(‘_’,’ ‘)にしても結果は’my name_is_Ron’になります。

なので、文字列の中で存在する全ても指定文字を変換したい時は正規表現を使うと行けます。

const mySentence = 'my name is:Ron';

mySentence.replace(':',' ');        //'my name is Ron'

const mySentence = 'my_name_is_Ron';

mySentence.replace('_',' ');      // 'my name_is_Ron'

//正規表現ですべての文字を変換
mySentence.replace(/_/g, ' ');    // 'my name is Ron'

indexOfは指定文字の位置を返します。

このメソッドは指定文字の前または後で文字列を切り取るとき有用なものです。

たとえ、’Z001-03’から後ろの’03’だけを抽出したいとき、indexOfで’-‘文字を探し、返しのindex +1位置から最後までカットすると’03’を切り取ることができます。

もし文字列の中指定文字が一つ以上の場合、頭から探し始め最初に見当たる文字位置を返します。

でもパラメータにindexを渡すと指定された位置から指定文字を探します。

注意点はstart indexを渡しても返す値は全体の文字列から何番目であるという数字を返します。

後ろから検索するのはlastIndexOfで、後ろから最初の指定文字の位置を返します。

この時も返す値は頭から何番目であるという数字です。

指定文字がなければ-1を返します。

const mySentence = 'Z001-03';

mySentence.indexOf('-');     // 4

mySentence.substring(mySentence.indexOf('-')+1);    // '03'

mySentence.indexOf('D');    // -1

const mySentence = 'Z001-03-02';

mySentence.indexOf('-');    // 4
mySentence.indexOf('-', 5);   // 7

mySentence.lastIndexOf('-')   // 7

testは一般的に正規表現と一緒に使い、渡す文字列と比較し有効性を確認します。

パスワード、アルファベット、特殊文字、生年月日などの形式検査に使えます。

const mySentence = /a/;
const myWord = 'baby';

mySentence.test(myWord);   // true

/\d/.test('cookie');       // false \dは数字を意味(正規表現)
/\d/.test('cookie1');      // true

上の例以外にも山ほどメソッドが多いし、また新しく作られるメソッドもたくさんありますね。

また必要な機能のメソッドがあれば確認していきましょ!

あざーす!

쉽고 빠르게 커링(Currying) 기법 이해하기(feat.자바스크립트)

커링(Currying) 기법의 이해와 사용, 그리고 장단점

커링(Currying)은 카레와 같은 스펠링을 갖고 있지만 그 유명한 하스켈(Haskell)이라는 이름의 출처인 수학&논리학자 하스켈 브룩스 커리(Haskell Brooks Curry)의 성(Family Name)입니다.

커링은 하나 이상의 매개변수(Parameter)를 갖는 함수를 부분적으로 나누어 각각 단일 매개변수를 갖는 함수로 설정하는 기법입니다.

식으로 설명하면 다음과 같습니다.

func(a, b, c) -> f(a)(b)(c)

위 식을 코드로 변경하면 함수를 리턴하면서 다음 매개변수를 받아 또 함수를 리턴합니다.

//non-curried
function plusFunc(a, b, c){
  console.log(a + b + c);
}

plusFunc(1, 2, 3);   // 6

//curried
function plusFunc(a){
    return function(b){
       return function(c){
          console.log(a + b + c);
       }
    }
}

plusFunc(1)(2)(4);  // 7

논커리와 커리의 차이를 확인해 보겠습니다.

non-curried : 함수 실행 시 파라미터가 모자라도 문제 없이 실행이 가능함
? 함수 정의 : func(a, b, c)
? 함수 실행 : func(a)
? 실행 결과 : func(a, undefined, undefined)

curried : 함수가 인수를 전부 받을 때까지 실행을 보류함.
? 함수 정의 : func(a, b, c)
? 함수 실행 : func(a)
? 실행 결과 : func(a)상태에서 b 함수 입력 대기

위 특징에 따라 다음과 같이 사용할 수 있습니다.

function setAppell(appell){
    return function(name) {
       console.log(appell + name);   
    }
}

const setName = setAppell('Mr.');
setName('Right');  // Mr.Right
setAppell('Mr.')('Baker'); //Mr.Baker

//ES6 화살표 함수
const setAppell = appell => name => console.log(appell + name);

const setName = setAppell('Miss.');
setName('Dior');  // Miss.Dior

또한 다음과 같이 이벤트와 파라미터를 동시에 전달할 때도 유용합니다.

const setNumber = (num) => (event) => {
     console.log(event.target.id+':'+num);
}

<div onClick="setNumber(2)(event)" id='myNum'>
  click
</div>

// myNum:2

그럼 커링의 장점을 요약하면 무엇이 있을까요?

장점?
재사용 가능
– 생산성 향상(코드 양 감소)
– 가독성 향상

그렇다면 커링의 단점은 무엇일까요?

단점?
– 함수가 깊이 깊이 중첩되면 메모리를 과다하게 점유할 가능성이 있는 것과 같은 맥락에서 커링을 과용하면
메모리와 속도에 문제점 발생 가능성이 있음

커링의 수학적인 설명이 필요하신 분을 위한 링크



커링을 사용하지 않아도 원하는 기능을 대부분 구현할 수 있지만 재사용성이나 가독성에 따른 생산성 향상을 위해 커링을 사용하면 좋은 효과를 기대할 수 있습니다. 커링을 직접 구현하지 않더라도 커링의 개념과 사용법에 대해 알아두면 다른 사람의 코드를 읽거나 로직을 이해하는데 큰 도움이 될 것이라 생각합니다.

target과 currentTarget, 차이가 뭘까?(feat.자바스크립트)

target과 currentTarget을 상황에 맞게 처리하기

자바스크립트에서 이벤트를 다룰 때 targetcurrentTarget의 프로퍼티를 사용해 요소에 접근이 가능합니다.

그럼 targetcurrentTarget의 차이는 무엇일까요?

1. target과 currentTarget

간단하게 설명하면 target은 이벤트가 발생한 바로 그 요소를 직접 가리키고 currentTarget이벤트 리스너(EventListener)를 가진 요소를 가리킵니다.

‘백문불여일코드(百聞不如一code)’이므로 코드를 통해 무엇을 의미하는지 확인해 보겠습니다.

<style>
  .upper{
    background:gold;
    width:80px;
    text-align:center;
  } 
  .lower{
    background:pink;
    width:50px;
  }
</style>

<script>
  function clicked(event){
    alert(event.target.id+" clicked");
  }
</script>

<div class="upper" onClick="clicked(event)" id="div">
  <span class="lower" id="span">                  
    span
  </span>
</div>

위의 코드를 통해 각각이 의미하는 바를 확인해 보겠습니다.

먼저 위 코드를 실행하면 다음 그림과 같은 결과가 나타납니다.

여기서 노랑은 div, 핑크는 span이며, 핑크가 노란색 위에 앉아있는 것이라고 볼 수 있습니다.

그런데 onClick 이벤트는 div에서 설정했지만 노랑을 눌러도, 핑크를 눌러도 모두 이벤트가 발생합니다.

div에만 이벤트를 설정했는데 왜 자식인 span도 이벤트를 상속 받는 것일까요?

이는 이벤트 버블링이벤트 캡쳐와 관련이 있으며, 관련 내용은 아래 포스트에서 확인해 보겠습니다.

이벤트 버블링과 이벤트 캡쳐 포스트

이벤트 발생에 따른 target은 다음과 같습니다.

– 핑크 부분을 클릭
target -> 핑크 (핑크를 눌렀으므로 핑크가 이벤트 발생 시점이 됨)
currentTarget -> 노랑 (onClick 이벤트는 노랑이 갖고 있음)

노란 부분을 클릭
target -> 노랑(노랑을 눌렀으므로 노랑이 이벤트 발생 시점이 됨)
currentTarget -> 노랑(onClick 이벤트는 노랑이 갖고 있음)

따라서 노랑을 누르면 ‘div clicked’, 핑크를 누르면 ‘span clicked’ 알림창을 띄웁니다.

만약 핑크를 눌러도 이벤트를 가진 노랑의 속성에 접근하고 싶다면 currentTarget과 getAttribute를 사용하면 됩니다.

<style>
  .upper{
    background:gold;
    width:80px;
    text-align:center;
  } 
  .lower{
    background:pink;
    width:50px;
  }
</style>

<script>
  function clicked(event){
    alert(event.currentTarget.getAttribute('id')+" clicked");
  }
</script>

<div class="upper" onClick="clicked(event)" id="div">
  <span class="lower" id="span">                  
    span
  </span>
</div>

위 코드는 핑크, 노랑 둘 다 이벤트를 가진 노랑 속성에만 접근합니다.

target은 누른 바로 그 것, currentTarget은 이벤트를 실행하는 바로 그 것으로 이해하면 될 것 같습니다.



targetcurrentTarget이벤트핸들링이벤트 캡쳐와 함께 이벤트 발생 관련하여 중요한 속성과 개념입니다.

백문불여일코드(code)이므로 코드를 통해 확인하고 연습하는 습관을 기르면 어제의 정보가 내일의 나의 무기가 될 수 있을 것이라고 생각합니다.

이벤트 버블링과 이벤트 캡쳐, 이제는 내 것으로 만들자(자바스크립트)

이벤트버블링과 이벤트캡쳐, 개념과 활용

1. 이벤트 버블링

(이벤트 버블링은 거품이 퐁퐁퐁퐁 일어나듯이 이벤트가 퐁퐁퐁퐁 일어나 ‘버블링(Bubbling)’이라는 이름을 갖게 되었다는 글을 어디선가 본 적이 있습니다..)

이벤트 버블링은 이벤트 발생 요소에서부터 순서대로 최상위 부모 요소까지 퐁퐁퐁퐁 이벤트가 연달아 발생합니다.

동작을 확인하기 위해 다음 코드의 결과를 보겠습니다.

<style>
  .upper{
    background:gold;
    width:70px;
    text-align:center;
  } 
  .middle{
    background:orange;
    width:50px;
    text-align:center;
  }
  .lower{
    background:pink;
    width:30px;
  }
</style>

<script>
  function clicked(event){
    alert(event.currentTarget.getAttribute('id'));
  }
</script>

<div class="upper" onClick="clicked(event)" id="쌉">a
  <div class="middle" onClick="clicked(event)" id="가">b
    <div class="lower" onClick="clicked(event)" id="능">c
    </div>
  </div>
  
</div>

위 코드를 실행하면 다음 그림과 같은 결과가 나옵니다.

여기서 가장 내부에 자리 잡고 있는 c를 클릭하면 어떤 순서대로 이벤트가 실행될까요?

이벤트 버블링이 default로 설정되어 있으므로 c, b, a의 순서대로 이벤트가 발생합니다.

그러므로 위 코드라면 ‘능’ -> ‘가’ -> ‘쌉’ 의 순서대로 알림창이 뜹니다.

b를 클릭하면 ‘가’ -> ‘쌉’ 의 순서로 알림창이 뜹니다.

그렇다면 순서를 ‘쌉’ -> ‘가’ -> ‘능’ 으로 만들려면 어떻게 해야 할까요? 바로 이벤트 캡쳐를 사용하면 됩니다.

2. 이벤트 캡쳐

이벤트 캡쳐를 사용하려면 addEventListener 내부에 capture 값을 명시적으로 true로 변경해줘야 합니다.

기본값은 false이며, false는 이벤트 버블링, true는 이벤트 캡쳐를 의미합니다.

function clicked(event){
    alert(event.currentTarget.getAttribute('id'));
}

const divs = document.querySelectorAll('div');
divs.forEach(function(div) {
	div.addEventListener('click', clicked,{
  	capture:true
  }
  );
});  

<div class="upper" id="쌉">a
  <div class="middle" id="가">b
    <div class="lower" id="능">c
    </div>
  </div>
</div>

위 코드에서 capture:true로 설정해주고 다시 c를 클릭하면 이번에는 반대 순서인 위에서 아래로 이벤트가 발생합니다.

이제는 ‘쌉’ -> ‘가’ -> ‘능’ 의 순서로 알림이 뜹니다.

그렇다면 ‘이벤트 버블링도 싫고, 이벤트 캡쳐도 싫으니 둘 다 하지마!’라고 명령할 때는 어떻게 해야 할까요?

간단하게 이벤트와 함께 실행될 함수 내부에 event.stopPropagation()을 넣어주면 됩니다.

프로파간다(propaganda)를 닮은 그 단어 ‘propagation’은 ‘번식, 증식, 전파’등의 의미를 갖고 있습니다.

따라서 ‘이벤트의 증식 또는 전파를 멈추겠다’는 의미로 event.stopPropagation() 메소드를 사용하면 됩니다.

<style>
  .upper{
    background:gold;
    width:70px;
    text-align:center;
  } 
  .middle{
    background:orange;
    width:50px;
    text-align:center;
  }
  .lower{
    background:pink;
    width:30px;
  }
</style>

<script>
  function clicked(event){
    event.stopPropagation();
    alert(event.currentTarget.getAttribute('id'));
  }
</script>

<div class="upper" onClick="clicked(event)" id="쌉">a
  <div class="middle" onClick="clicked(event)" id="가">b
    <div class="lower" onClick="clicked(event)" id="능">c
    </div>
  </div>
  
</div>

위 메서드는 더 이상 ‘쌉 가 능’ 또는 ‘능 가 쌉’ 따위의 단어를 완성하도록 내버려두지 않습니다.

c를 눌러도 ‘쌉’ 또는 ‘능’만 띄울 뿐입니다.



이벤트핸들링이벤트 캡쳐는 의도한 대로 이벤트를 발생시키기 위한 중요한 속성과 개념입니다.

위 개념은 다음 포스트와 관련이 있습니다.

target, currentTarget 차이가 뭘까?

백문불여일코드(code)이므로 코드를 통해 확인하고 연습하는 습관을 기르면 어제의 정보가 내일 나의 무기가 될 수 있을 것이라 생각합니다.

ジャバスクリプトで配列を結合(マージ)する良い方法(javascript)

配列を結合する便利な三つのやり方(concat(), …spread, push())

ジャバスクリプトを使ってデータを扱うことが多くなり、配列を使用する仕組みも増えていきます。

その中、マージ(結合)についてちらっと調べてみますね。


配列(array)はインデックスを持つ要素を順に並べる構造です。

一つの配列ならインデックスまたはメソッド(unshift(), push(), shift(), pop(), splice(), find(), includes(), join(), indexOf()など)を使って配列中のデータを便利に操作することができます。

配列間のマージ方法も同じくconcat(), push(), …spreadなどが存在します。

一つづつ確認してみます。

1. 配列結合の三つ方法

concatenate(連結する意味)の略字であるconcat()は意味通り配列両方を繋ぎます。

次のように扱うので、サンプルから確認してみます。

  • [array1].concat([array2]…..)
  • [].concat([array1], [array2]……)
  • const color1 = ['blue', 'pink', 'white'];
    const color2 = ['black', 'orange', 'yellow'];
    const num1 = [100, 200, 300];
    const num2 = [400, 500, [600, 700]];
    
    const allColor = color1.concat(color2); 
    //['blue', 'pink', 'white', 'black', 'orange', 'yellow']
    console.log(allColor); 
    
    const colorPlus = color1.concat('gold', ['silver', 'bronze']); 
    //['blue', 'pink', 'white', 'gold', 'silver', 'bronze']
    console.log(colorPlus);
    
    const colorAndNum = color1.concat(num1);   
    //['blue', 'pink', 'white', 100, 200, 300]
    console.log(colorAndNum); 
    
    const allNum = num1.concat(num2);   
    //[100, 200, 300, 400, 500, [600, 700]]
    console.log(allNum); 
    

    concatは一つ以上の配列を結合し、一つの配列にします。

    配列中のデータの形式(タイプ)にかかわらず結合できるし、ネストされている配列(配列中にまた配列がある構造)の結合もできます。


    ES6から追加された文法の一つであるスプレッド構文は配列或はオブジェクトを速くて安全にコピーすることができます。

    深いコピー(deep copy)方法でコピーするので、既存のデータとは違うメモリーアドレスを使いますね。

    なので配列の結合方法の中では一番いい方法だと言えます。

    const musician = ['Chet Baker', 'Miles Davis'];
    const actor = ['Clint Eastwood', 'George Clooney'];
    
    const favorites = [...musician, ...actor];    
    // ['Chet Baker', 'Miles Davis', 'Clint Eastwood', 'George Clooney'];
    console.log(favorites); 
    
    const favoritesReverse = [...actor, ...musician];
    // ['Clint Eastwood', 'George Clooney', 'Chet Baker', 'Miles Davis'];
    console.log(favoritesReverse)
    
    

    また、スプレッド構文は次のように非構造化ではよくつかわれています。

    const myColor = ['beige', 'burgundy', 'charcoal', 'ivory'];
    
    const [color1, color2, ...others  ] = myColor;
    console.log(color1);   // 'beige'
    console.log(color2);   // 'burgundy'
    console.log(others);   // ['charcoal', 'ivory']

    push()は配列の最後に要素を追加するときよく使うメソッドです。

    push()メソッドとスプレッド構文を一緒に使うと配列の結合ができます。

    でもこの方法は既存データの上に新しいデータを上書きするので既存データの変更に注意する必要があります。

    const pizza = ['dough', 'cheese', 'meat', 'source'];
    const pizzaAdd = ['parmesan', 'tabasco'];
    
    pizza.push(...pizzaAdd);   
    
    // ['dough', 'cheese', 'meat', 'source', 'parmesan', 'tabasco'];
    console.log(pizza); 
    

    2. 総合

    配列を結合する方法の中concat(), …spread, push()について調べてみました。

    状況により必要な方法が違うので、システムやロジックを考えた上で選んだら問題はないと思います。

    あざーす!

    자바스크립트, 편리한 배열 병합 방법(Merge Arrays in Javascript)

    자바스크립트의 배열 병합 방법(concat(), …spread, push())

    자바스크립트를 사용해 데이터를 다루는 일이 점점 더 많아지고, 배열을 사용하는 관련 작업을 많이 하게 됩니다.

    그 중에서도 병합과 관련된 부분에 대해 알아보도록 하겠습니다.


    배열은 인덱스(Index)를 갖는 요소를 순서대로 나타낸 데이터 구조입니다.

    하나의 배열이라면 인덱스 또는 메소드(unshift(), push(), shift(), pop(), splice(), find(), includes(), join(), indexOf() 등)를 사용해 배열 내부의 데이터를 쉽게 다룰 수 있죠.

    배열의 병합(Merge) 또한 마찬가지입니다.

    concat(), push(), 스프레드 연산자(…spread), 등 배열의 병합을 위한 여러 방법이 존재합니다.

    하나씩 확인해 보겠습니다.

    1. 배열 병합의 3가지 방법

    concatenate(연결시키다, 사슬같이 잇다)의 약자인 concat()은 말 그대로 두 배열을 연결하는 역할을 합니다.

    크게 다음과 같은 방식으로 사용하며, 샘플 코드를 통해 조금 더 상세한 부분을 확인하겠습니다.

    • [array1].concat([array2]…..)
    • [].concat([array1], [array2]……)
    const color1 = ['blue', 'pink', 'white'];
    const color2 = ['black', 'orange', 'yellow'];
    const num1 = [100, 200, 300];
    const num2 = [400, 500, [600, 700]];
    
    const allColor = color1.concat(color2); 
    //['blue', 'pink', 'white', 'black', 'orange', 'yellow']
    console.log(allColor); 
    
    const colorPlus = color1.concat('gold', ['silver', 'bronze']); 
    //['blue', 'pink', 'white', 'gold', 'silver', 'bronze']
    console.log(colorPlus);
    
    const colorAndNum = color1.concat(num1);   
    //['blue', 'pink', 'white', 100, 200, 300]
    console.log(colorAndNum); 
    
    const allNum = num1.concat(num2);   
    //[100, 200, 300, 400, 500, [600, 700]]
    console.log(allNum); 
    

    위와 같이 concat은 하나 이상의 배열을 합쳐 하나의 배열로 만듭니다.

    배열 내부 데이터 타입에 상관없이 병합이 가능하며, 중첩된 배열의 병합도 가능합니다.

    ES6에서 추가된 문법인 스프레드 연산자는 배열이나 객체를 빠르고 안전하게 복사할 수 있습니다.

    깊은 복사(Deep copy) 방식으로 복사를 진행해 기존의 배열 데이터와는 다른 메모리 주소를 사용하므로 배열 병합 방법 중에서는 가장 좋은 방식이라고 볼 수 있습니다.

    const musician = ['Chet Baker', 'Miles Davis'];
    const actor = ['Clint Eastwood', 'George Clooney'];
    
    const favorites = [...musician, ...actor];    
    // ['Chet Baker', 'Miles Davis', 'Clint Eastwood', 'George Clooney'];
    console.log(favorites); 
    
    const favoritesReverse = [...actor, ...musician];
    // ['Clint Eastwood', 'George Clooney', 'Chet Baker', 'Miles Davis'];
    console.log(favoritesReverse)
    
    

    스프레드 연산자는 다음과 같이 비구조화 할당에서도 자주 사용됩니다.

    const myColor = ['beige', 'burgundy', 'charcoal', 'ivory'];
    
    const [color1, color2, ...others  ] = myColor;
    console.log(color1);   // 'beige'
    console.log(color2);   // 'burgundy'
    console.log(others);   // ['charcoal', 'ivory']

    push() 메소드는 배열 끝에 데이터를 추가할 때 자주 사용하는 메소드입니다.

    push() 메소드와 스프레드 연산자를 같이 사용하면 배열 병합이 가능하지만 기존 배열에 데이터를 추가하는 방식이므로 기존 배열이 변경되는 부분을 주의해야 합니다.

    const pizza = ['dough', 'cheese', 'meat', 'source'];
    const pizzaAdd = ['parmesan', 'tabasco'];
    
    pizza.push(...pizzaAdd);   
    
    // ['dough', 'cheese', 'meat', 'source', 'parmesan', 'tabasco'];
    console.log(pizza); 
    

    2. 마무리

    배열을 병합하는 여러 가지 방법 중 concat(), …spread, push() 세 가지를 소개하였습니다.

    각 상황에 따라 필요한 방법이 다르니 필요에 따라 맞는 것을 골라 사용하면 좋을 것 같습니다.

    감사합니다!

    자바스크립트, 문자열 함수 마음대로 다루기(Javascript works with string)

    Javascript String Methods : slice(), substring(), includes(), startsWith(), trim(), replace()…….

    프론트엔드(React, Vue, Angular)와 백엔드(Node.js)가 모두 자바스크립트를 사용하는 시대에 들어서니 자바스크립트의 기초가 몇 배는 더 중요해진 것 같습니다.

    자바스크립트의 문법 중 문자열(String) 관련 함수는 참 쓸 일이 많으면서도 가볍게 여겨져 쉽게 외워지지 않는 슬픈 함수 중 하나인 것 같습니다.?

    문자열 관련 함수는 한 번 외워두면 반복 학습할 일이 많으므로 첫 단추만 잘 꿰면 될 것 같습니다.

    그럼 문자열 관련하여 사용 빈도가 높은 함수인 slice(), substring(), includes(), startsWith(), trim(), replace(), indexOf(), test() 함수에 대해 알아보겠습니다.


    slice 함수는 위치(인덱스)를 매개변수로 전달하여 원하는 문자열을 추출합니다.

    시작 위치(필수)와 종료 위치(옵션)를 전달하며, 시작 위치만 전달하는 경우 시작 위치부터 문자열 끝까지 추출합니다.

    0은 순방향으로 첫 번째(앞에서 1번 째), 음수인 -1은 역방향으로 첫 번째(뒤에서 1번 째)를 나타냅니다.

    const myWord = 'primavera';
    
    myWord.slice(0,5);   // 'prima'
    myWord.slice(-4);    // 'vera'
    myWord.slice(3);     // 'mavera'
    
    myWord.slice(3,1);    // '' -> slice함수는 시작인덱스가 종료인덱스보다 크면 빈 값을 반환

    substring 함수도 slice와 마찬가지로 위치(인덱스)를 매개변수로 전달하여 원하는 문자열을 추출합니다.

    기본적인 작동 방식은 같으나 slice와 달리 시작인덱스가 종료인덱스보다 큰 경우에는 자동으로 두 값의 위치를 변경하여 함수를 실행합니다.

    또한 substring은 매개변수에 음수를 넣으면 이 값을 0으로 인식합니다.

    따라서 (3, -1)을 전달하게 되면 (3, 0)으로 인식합니다. (3, 0)은 시작 인덱스가 더 크므로 위치를 변환하여 (0, 3)의 값을 계산한 결과를 반환합니다.

    함수명이 비슷한 substr의 경우 ‘위치’와 ‘추출할 문자의 수’를 파라미터로 전달하는 함수입니다.

    const myWord = 'invierno';
    
    myWord.substring(3);     // 'ierno'
    myWord.substring(1,3);   // 'nv'
    
    myWord.substring(3,1);   // 'nv' -> 시작이 종료인덱스보다 크면 자동으로 위치 변경 후 함수를 실행
    
    myWord.substring(3,-1);  // 'inv' -> 음수는 0, 시작인덱스와 종료인덱스는 위치 변경 후 계산
    
    myWord.substring(-4);    // 'invierno' -> 음수는 0, 시작인덱스만 전달하여 0부터 끝까지 계산
    
    myWord.substr(1,3);      // 'nvi' -> 시작인덱스 1부터 문자 3개를 추출
    

    includes 함수는 지정한 문자열의 포함 여부를 확인합니다.

    매개변수로 문자열(필수)과 위치인덱스(옵션)를 전달하며, 반환값은 true 또는 false 입니다.

    문자열 내 지정 문자열 확인은 물론 배열 내 빈 값을 확인하는 용도로도 사용할 수 있습니다.

    const myWord = 'alegria';
    
    myWord.includes('le');       // true
    myWord.includes('i');        // true
    myWord.includes('rio');      // false
    
    const myArray = ['dream','','king'];
    myArray.includes('');       // true   -> myArray[1]이 빈 값이므로 true. 배열 내 빈 값을 찾는 용도
    myArray.includes('dream');  // true   -> myArray[0]의 값과 일치
    myArray.includes('drea');   // false  -> 배열 내 값과 정확하게 일치하는 경우에만 true를 반환
    

    startsWith 함수도 문자열의 포함 여부를 확인하는 함수라고 볼 수 있습니다.

    startsWith와 함께 문자열만 전달하면 0 인덱스부터 시작하여 해당 문자열의 존재 여부를 확인하여 BOOLEAN 값을 반환합니다.

    마찬가지로 endsWith는 끝나는 문자열을 비교합니다.

    const mySentence = 'cafe con leche';
    
    mySentence.startsWith('cafe');     // true
    mySentence.startsWith('fe');       // false
    mySentence.startsWith('fe',2);     // true
    
    mySentence.endsWith('eche');      // true
    

    trim 함수는 문자열 양 끝의 공백을 제거하는 함수입니다.

    문자열 변환 과정이나 실수로 보이지 않는 공백을 삽입하게 되면 문자열 비교 시 겉으로는 동일하지만 결과는

    false를 반환하므로 이 trim 함수를 사용하면 문제를 해결할 수 있습니다.

    문자열 양 끝의 공백, 탭, 줄바꿈 문자(/n)를 제거하며, 문자열 양 끝이 아닌 부분에는 효과가 없습니다.

    const mySentence = '  la casa de papel   ';
    
    mySentence.trim();        // 'la casa de papel'
    
    const mySentence = ' el amor \n';
    
    mySentence.trim();       // 'el amor'

    replace 함수는 문자열에서 변경할 기존문자를 찾아 변경문자로 변경합니다.

    문자열에서 매개변수로 전달한 기존문자가 여러 개 존재하는 경우 첫 번째로 탐색한 문자열만 변경합니다.

    예를 들어, ‘my_name_is_Ron’에서 ‘_’문자를 모두 공백으로 변경하기 위해 replace(‘_’,’ ‘) 함수를 사용해도 결과는 ‘my name_is_Ron’이 반환됩니다.

    따라서 문자열에서 해당하는 문자를 모두 변경하고 싶은 경우에는 정규표현식을 사용하면 됩니다.

    정규표현식 관련 포스트

    const mySentence = 'my name is:Ron';
    
    mySentence.replace(':',' ');        //'my name is Ron'
    
    const mySentence = 'my_name_is_Ron';
    
    mySentence.replace('_',' ');      // 'my name_is_Ron'
    
    //정규표현식 사용해서 모든 문자 변경하기
    mySentence.replace(/_/g, ' ');    // 'my name is Ron'

    indexOf 함수는 전달하는 문자의 위치를 반환합니다.

    이 기능은 원하는 문자열의 앞 또는 뒤에서 문자열을 자를 때 유용하게 사용할 수 있습니다.

    예를 들어, ‘Z001-03’이라는 문자열에서 뒤의 ’03’만 추출하고 싶은 경우 indexOf로 ‘-‘ 문자열의 위치를 찾고 해당 위치의 +1 위치부터 끝까지 문자열을 잘라내면 원하는 값을 가져올 수 있습니다.

    만약 같은 문자가 여러 개 존재하는 경우 첫 번째 문자의 위치를 반환하며, 두 번째 매개변수에 시작 위치를 전달하면 시작 위치부터 검색을 시작합니다(시작 위치를 전달하더라도 반환 값은 전체 문자열에서 몇 번째인지를 전달합니다).

    뒤에서부터 검색을 시작하는 함수는 lastIndexOf이며, 뒤에서부터 첫 번째로 나오는 문자의 위치를 반환합니다(반환 값은 해당 문자가 앞에서부터 몇 번째인지를 반환합니다).

    전달하는 문자가 존재하지 않으면 -1을 반환합니다.

    const mySentence = 'Z001-03';
    
    mySentence.indexOf('-');     // 4
    
    mySentence.substring(mySentence.indexOf('-')+1);    // '03'
    
    mySentence.indexOf('D');    // -1
    
    const mySentence = 'Z001-03-02';
    
    mySentence.indexOf('-');    // 4
    mySentence.indexOf('-', 5);   // 7
    
    mySentence.lastIndexOf('-')   // 7
    

    test 함수는 정규표현식과 함께 사용하여 매개변수로 전달하는 문자열의 유효성을 확인합니다.

    비밀번호에 알파벳, 특수문자 등 포함여부 확인이나 생년월일 형식에 알파벳 등 불필요한 문자의 포함 여부를 효과적으로 확인할 수 있습니다.

    const mySentence = /a/;
    const myWord = 'baby';
    
    mySentence.test(myWord);   // true
    
    /\d/.test('cookie');       // false \d는 숫자(0~9)를 나타내는 정규표현식
    /\d/.test('cookie1');      // true
    
    const typedError = /[ㄱ-ㅎㅏ-ㅣ]/gi;
    const inputData = '김ㅊ치와 나물';
    
    typedError.test(inputData);    // true 

    이 외에도 다양한 함수들이 많고 또 여러 요구에 맞춰 새롭고 편리한 함수가 계속 나오기 때문에 끊임없는 관심과 업데이트 관련하여 모니터링이 필요할 것 같습니다.

    자바스크립트 비구조화 할당 사용하기(Beautiful Destructuring in JS)

    자바스크립트에서 객체 비구조화(Object Destructuring)를 사용하는 아름다운 방법
    from. How to Use Object Destructuring in Javascript(Dmitri Pavlutin)

    비구조화 할당(Destructuring Assignment)이란 배열 또는 객체 안의 속성값를 풀어 편리하게 변수에 넣는 것을 말합니다.
    한 번 선언으로 여러 속성값 추출, 하부 객체에서 접근, 값이 존재하지 않는 경우 기본값 설정 등이 가능합니다.

    백 번의 설명보다 하나의 샘플 코드가 더 유용하므로 하나씩 알아보도록 하겠습니다.

    1. 비구조화 할당의 필요성

    자바스크립트 ES2015 환경에서 객체의 속성값을 가져오려면 다음과 같은 코드를 사용합니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const name = flower.name;
    const color = flower.color;
    
    console.log(name);    // 'Rose'
    console.log(color);    //  'Red'

    flower.name 값을 name 변수에 넣고, flower.color 값을 color 변수에 넣습니다.

    하지만 위와 같이 값을 대입하면 반복되는 부분이 발생합니다. 바로 ‘name’과 ‘color’입니다.

    대입 과정에서 name이 변수 선언에 한 번, 그리고 flower 객체의 속성 접근에 한 번, 총 두 번이 사용됩니다.

    color도 마찬가지입니다.

    비구조화 할당은 이런 번거로움을 없애고자 할 때 사용합니다.

    비구조화 할당을 사용하면 객체에서 속성을 가져와 같은 이름의 변수에 대입할 때 속성명을 한번 더 명시할 필요가 없습니다.

    게다가 속성이 여러 개일지라도 한번의 선언으로 해결이 가능합니다.

    아래 코드에서는 위 코드를 리팩토링하여 비구조화 할당을 사용합니다.

    const flower = {
     name: 'Rose',
     color: 'Red'
    };
    
    const { name, color } = flower;
    
    console.log(name);   // 'Rose'
    console.log(color);  // 'Red'

    ‘const { name, color } = flower’가 바로 객체 비구조화입니다.
    이 구문으로 name과 color 변수를 선언하고, flower.name과 flower.color 속성의 값을 같은 이름 변수에 할당합니다.

    다음은 객체의 속성값에 접근하는 두 가지 방법입니다.

    const name = flower.name;
    const color = flower.color;
    
    // 위와 아래는 같은 결과
    
    const { name, color } = flower;

    속성명과 변수명이 중복되지 않으므로 비구조화가 훨씬 깔끔하고 편리한 것을 알 수 있습니다.

    2. 속성값 가져오기

    객체 비구조화의 기본 문법은 간단합니다.

    const { identifier } = expression;

    여기서 identifier는 속성에 접근할 이름이고, expression은 identifier가 접근하는 객체입니다.

    비구조화를 하면 identifier 변수는 접근한 속성값을 갖게 됩니다.

    위와 같은 코드이지만 속성 접근자를 사용하게 되면 다음과 같습니다.

    const identifier = expression.identifier;

    위 내용을 토대로 비구조화 할당을 연습해 보겠습니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const { name } = flower;
    
    console.log(name);  // 'Rose'

    ‘const { name } = flower;’는 name 변수를 선언하고, flower.name의 속성값을 대입하는 비구조화 할당입니다.

    3. 여러 속성값 한번에 가져오기

    여러 속성값을 가진 객체의 비구조화는 쉼표를 사용하면 됩니다.

    const { identifier1, identifier2, ..., identifierN } = expression;

    여기서 identifier1, identifier2, …, identifierN은 속성에 접근할 이름이고, expression은 identifier가 접근하는 객체입니다. 비구조화를 하면 각 identifier 변수는 접근한 속성값을 갖게 됩니다.

    속성 접근자를 사용하는 코드는 다음과 같습니다.

    const identifier1 = expression.identifier1;
    const identifier2 = expression.identifier2;
    // ......
    const identifierN = expression.identifierN;

    그럼 두 개의 속성값을 할당하는 첫 번째 샘플 코드를 다시 확인해 보겠습니다.

    const flower = {
     name: 'Rose',
     color: 'Red'
    };
    
    const { name, color } = flower;
    
    console.log(name);   // 'Rose'
    console.log(color);  // 'Red'

    ‘const { name, color } = flower;’는 name, color 두 변수를 생성하고, 각각 flower.name과 flower.color의 속성값을 할당합니다.

    4. 기본값 설정하기

    비구조화를 하더라도 객체에 동일한 속성명이 존재하지 않는 경우 undefined가 할당됩니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const { meaning } = flower;
    
    console.log(meaning);  // undefined 

    위와 같이 속성이 존재하지 않는 경우 기본값 설정이 가능하며, 문법은 다음과 같습니다.

    const { identifier = defaultValue } = expression;

    identifier가 expression 객체의 속성에 접근하는 이름이 됩니다. expression 객체 내 identifier 속성이 존재하지 않으면 지정한 기본값인 defaultValue가 대입됩니다.

    다음 코드와 같은 기능을 합니다.

    const identifier = expression.identifier === undefined ? defaultValue : expression.identifier;

    기본값 설정 옵션을 더해 코드를 수정해 보겠습니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    }
    
    const { meaning = 'love' } = flower;
    
    console.log(meaning)  // 'love'

    undefined 대신 ‘love’가 대입됩니다.

    5. 별명(alias) 정하기

    객체의 속성명과 다른 이름으로 변수를 선언하고 싶을 때는 별명을 지정하면 됩니다.

    const { identifier: aliasIdentifier } = expression;

    여기서 identifier는 속성에 접근하는 이름이며, aliasIdentifier는 변수로 사용할 이름입니다. 비구조화를 하면 aliasIdentifier가 속성값을 갖습니다.

    다음 코드와 같은 의미입니다.

    const aliasIdentifier = expression.identifier;

    실제 별명을 적용한 코드는 다음과 같습니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const { color: realColor } = flower;
    
    console.log(realColor); // 'Red'
    console.log(color); // 에러 발생(color is not defined)

    위 코드는 realColor로 변수를 선언하고, flower.color값을 할당하여 비구조화를 진행합니다.

    6. 내부 객체의 속성값 가져오기

    앞에서 확인한 샘플은 원시형 데이터 타입(String, Number 등)의 속성을 갖는 일반 객체입니다.

    그러나 일부는 객체 안에 또 다른 객체를 갖습니다. 물론 이러한 형태를 가진 객체도 비구조화 할당이 가능합니다.

    const { nestedObject: { identifier } } = expression;

    nestedObject는 내부 객체명이고, identifier는 내부 객체의 속성명입니다.

    비구조화 할당을 통해 identifier가 내부 객체의 속성값을 가지며, 다음 코드와 같은 의미입니다.

    const identifier = expression.nestedObject.identifier;

    내부 객체의 깊이는 제한이 없으며, 중괄호{ }만 추가해주면 됩니다.

    const { propA: { propB: { propC: {......} } } } = object;

    다음 샘플을 통해 내부 객체에 접근하여 비구조화 할당을 진행하는 코드를 확인해 보겠습니다.

    const flower = {
      name: 'Rose',
      color: 'Red',
      blooming: {
       season: 'spring'
      }
    };
    
    //Object destructuring
    const { blooming: { season } } = flower;
    
    console.log(season) // 'spring'

    ‘const { blooming: { season } } = flower;’는 내부 객체 blooming의 season 속성값을 할당합니다.

    7. 동적 이름으로 속성값 가져오기

    동적 이름(Dynamic Name)의 사용도 가능합니다.

    const { [propName]: identifier } = expression;

    propName은 속성명(주로 string 타입)이며, identifier는 값이 할당될 변수의 이름입니다.

    다음 코드와 같은 의미를 갖습니다.

    const identifier = expression[propName];

    다음 샘플을 통해 속성명으로 사용되는 prop을 확인하겠습니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const prop = 'name';
    const { [prop]: flowerName } = expression;
    
    console.log(flowerName); // 'Rose'

    ‘const { [prop]: flowerName } = expression’에서 flowerName 변수에 flower[prop] 속성값을 할당하며, prop은 접근할 속성명을 가진 변수입니다.

    8. 비구조화 할당 후 남겨진 객체

    나머지 데이터를 관리하는 문법은 비구조화 할당에서 유용하게 사용할 수 있습니다.

    const { identifier, ...rest } = expression;

    위 코드에서 identifier를 제외한 나머지 속성은 rest 변수에 할당됩니다.

    아래 코드에서 name 속성을 제외한 나머지 속성을 정리하는 샘플을 확인해 보겠습니다.

    const flower = {
      name: 'Rose',
      color: 'Red'
    };
    
    const { name, ...realColor } = flower;
    
    console.log(realColor) // { realColor: 'Red' }

    name 속성값을 할당한 뒤 나머지 속성들은 realColor 변수에 할당합니다.

    9. 자주 사용되는 구문

    앞에서 확인한대로 비구조화는 속성값을 변수에 할당합니다.

    변수 타입은 const, let, var 모두 사용이 가능하며, 이미 선언된 변수에도 할당이 가능합니다.

    let을 사용한 방법은 다음과 같습니다.

    //let
    const flower = {
      name: 'Rose',
    };
    
    let { name } = flower;
    
    console.log(name); // 'Rose'

    var를 사용한 방법은 다음과 같습니다.

    //var
    const flower = {
      name: 'Rose',
    };
    
    var { name } = flower;
    
    console.log(name); // 'Rose'

    이미 선언된 변수에 할당하는 방법은 다음과 같습니다.

    //existing variable
    
    let name;
    
    const flower = {
      name: 'Rose',
    };
    
    ({ name } = flower);
    
    console.log(name); // 'Rose'

    for…of 반복문을 사용한 비구조화 할당은 다음과 같습니다.

    const flowers = [
      { name: 'Rose' },
      { name: 'Lily' }
    ];
    
    for (const { name } of flowers) {
      console.log(name) //'Rose', 'Lily'
    }

    사실 비구조화는 할당이 가능한 곳이면 어디든지 사용이 가능하다고 볼 수 있습니다.

    예를 들면 다음과 같이 함수 파라미터의 비구조화도 가능합니다.

    const flowers = [
      { name: 'Rose' },
      { name: 'Lily' }
    ];
    
    const names = flowers.map(
      function({ name }) {
        return name;
      }
    )
    
    console.log(names); // ['Rose', 'Lily']

    ‘function({ name })’은 함수의 파라미터를 비구조화하여, name 변수에 name 속성을 할당합니다.

    10. 변수 값 서로 교환(swap)

    비구조화 할당을 사용하면 두 변수의 값을 편리하게 교환할 수도 있으며, 기존처럼 데이터를 임시로 보관할 변수가 필요하지 않습니다.

    let a = 1;
    let b = 2;
    
    //기존 교환 방법
    const temp = a;
    a = b;
    b = temp;
    
    //비구조화를 통한 교환
    [a, b] = [b, a];

    11. 정리

    비구조화 할당은 객체의 속성값을 빠르고 쉽게 변수에 할당할 수 있는 아주 강력한 기능을 제공합니다.
    특히 간결한 문법과 여러 변수를 한 번에 선언할 수 있는 점이 매력적입니다.


    비구조화 할당에 대해 간결하면서도 누구나 이해하기 쉽도록 작성된 좋은 글을 접하게 되어 좋은 정보를 나누고자 양해를 구하여 국내에 맞게 번역하고 일부 내용을 추가하여 글을 올리게 되었습니다. 궁금하신 사항은 댓글 남겨주세요.

    출처: https://dmitripavlutin.com/javascript-object-destructuring/

    자바스크립트 클로저 파헤치기(What is Javascript Closure)

    Venezia

    자바스크립트에서 변수는 전역(global)변수와 지역(local)변수가 있어요.

    전역변수는 위치에 상관없이 모든 함수가 접근 가능하지만 지역변수는 해당 스코프 내부에서만 접근이 가능합니다.

    하지만 클로저를 사용하면 전역변수처럼 지역변수를 사용할 수 있어요. 안전성을 보장하면서요.

    전역변수

    함수는 함수 내 선언된 모든 변수에 접근할 수 있어요.

    샘플1


    function testFunction( ) {

      let abc = 10;

       return abc * 2;

    }


    또한 함수 외부의 변수에도 접근할 수 있어요.

    샘플2


    let abc = 10;

    function testFunction( ) {

       return abc * 2;


    샘플2에서 abc는 전역변수입니다. 웹 페이지에서 전역 변수는 window 객체에 속합니다. 

    전역변수는 페이지 내 모든 스크립트가 접근하여 사용하거나 변경할 수 있어요.

    샘플1에서 abc는 지역변수입니다. 지역변수는 변수를 선언한 함수 내에서만 사용이 가능해요. 

    다른 함수나 스크립트 코드에게는 감춰져 있는 것이죠.

    전역변수와 지역변수는 이름이 같아도 다른 변수입니다. 하나를 수정해도 다른 변수는 영향을 받지 않죠.

    선언자(var, let, const) 없이 생성된 변수는 모두 전역변수입니다. 특정 함수 안에서 생성 되었다고 해도 말이죠.

    function testFunction() {

       abc = 10;

    }

    변수의 생명 주기(lifetime)

    전역변수는 페이지가 종료될 때(다른 페이지로 넘어가거나 윈도우 창을 종료)까지 유효합니다.

    그리고 지역변수는 함수가 호출될 때 생성되어 함수가 종료될 때까지 유효합니다.

    숫자 카운팅의 문제

    모든 함수에서 접근이 가능한 변수를 만들어 숫자 카운팅에 사용해보도록 하겠습니다.

    전역 변수와 함수를 사용해 숫자를 카운팅합니다.

    샘플3


    let cnt = 0;

    function addNum(){

       cnt += 1;

    }

    addNum();

    addNum();      

    // 결과:2


    하지만 위의 코드는 addNum() 함수가 아닌 다른 코드로도 cnt 변수에 접근하여 수정이 가능합니다. 

    따라서 cnt 변수는 addNum() 함수 내부에 존재해야 다른 코드에서 접근할 수 없습니다.

    샘플4


    let cnt = 0;

    function addNum() {

       let cnt = 0;

      cnt += 1;

    }

    addNum();

    addNum();

    // 결과는 2일 것 같지만 0이 반환


    같은 이름의 전역변수를 선언하였기 때문에 결과는 2가 아닌 0이 됩니다. 

    그럼 함수의 반환값을 사용해 전역변수를 사용하지 않는 접근 방법을 확인해보겠습니다.

    샘플5


    function addNum() {

       let cnt = 0;

       cnt += 1;

       return cnt;

    }

    addNum();

    addNum();

    // cnt는 2가 되어야 하지만 결과는 1이 반환


    함수 내부에 선언한 지역변수는 함수 호출 시마다 리셋되므로 의도한대로 작동하지 않습니다.

    자바스크립트는 이 문제를 내부 함수(inner function)를 사용해 해결합니다.

    자바스크립트의 중첩 함수(Nested function)

    모든 함수는 전역 스코프에 접근할 수 있습니다. 

    그리고 자바스크립트의 모든 함수는 자신을 감싸고 있는 부모 스코프에 접근이 가능합니다. 

    자바스크립트는 중첩 함수를 지원하며, 중첩 함수 역시 자신을 감싸고 있는 부모 스코프에 접근이 가능합니다.

    아래 샘플을 확인해보면 내부함수 plus()는 부모 함수에 있는 cnt 변수에 접근이 가능합니다.

    샘플6


    function addNum() {

       let cnt = 0;

       function plus() {

          cnt += 1;

       }

       plus();  

       return cnt;

    }


    cnt = 0을 한번만 선언할 방법과 plus() 함수에 접근할 수 있는 방법을 찾으면 위에서 확인한 숫자 카운팅의 딜레마 문제를 해결할 수 있습니다.

    여기서 우리는 클로저를 사용할 수 있습니다.

    클로저를 확인하기 전에 먼저 즉시실행함수(IIFE, Immediately Invoked Function Expressions)에 대해 간략히 알아보겠습니다.

    즉시실행함수는 호출없이 자동으로 실행되는 함수로, 함수를 괄호로 묶고 반드시 끝에 ()를 붙여야 합니다.

    아래는 즉시실행함수의 표현식 샘플입니다.

    샘플7


    (function() {

      let abc = “self-invoking”;

    })();


    그럼 이제 클로저를 확인해보겠습니다.

    Austria(2013)

    자바스크립트 클로저(Closure)

    클로저는 즉시실행함수를 이용합니다.

    샘플8


    const addNum = (function() {

       let cnt = 0;

       return function() {

       cnt += 1

       return cnt;

       })();

    addNum();

    addNum();

    // 결과:2


    addNum 변수에는 즉시실행함수의 반환값이 할당됩니다. 

    즉시실행함수는 한번만 실행되며, cnt를 0으로 선언한 후 함수 표현식을 반환합니다.

    이를 통해 addNum은 함수가 되며, 부모 스코프가 종료되어도 부모 스코프에 존재하는 cnt 변수에 접근이 가능하게 됩니다.

    이것이 바로 자바스크립트의 클로저입니다. 클로저는 이와 같이 함수에서 상태 유지가 가능한 개별적인 변수들을 갖도록 합니다. 

    cnt는 익명함수의 스코프에 의해 보호받으며, 오직 addNum 함수만을 사용해 접근이 가능합니다.


    클로저는 부모 함수가 종료된 후에도 부모 스코프의 변수에 접근이 가능한 함수로, 특정 함수만 변수에 접근할 수 있도록 하기 위한 안전 장치라고 보면 됩니다. 
    참고로 클로저로 접근하는 변수와 부모 스코프에 존재했던 변수는 완전히 같은 공간을 참조하는 것은 아니고 각각 독립적인 객체입니다.


    참고자료 https://www.w3schools.com/js/js_function_closures.asp