반응형

화살표 함수(Arrow Function)


화살표 함수는 기존 function 표현에 비해 구문이 짧고, 화살표 함수는 항상 익명입니다. 이 함수 표현은 메소드 함수가 아닌 곳에 가장 적당합니다. 그래서 생성자로서 사용할 수 없습니다. function 키워드 대신 화살표(=>)를 사용하여 함수를 선언하는 방법입니다. 콜백 함수에서 사용하면 아주 간결하게 표현이 가능한 장점이 있습니다.


간단하게 화살표 함수를 정리하는 글이니 더욱 자세한 내용은 MDN을 참조해주세요.


표현식

1
2
3
4
5
// 함수 표현식 
function () {}
 
// 화살표 함수 표현식
() => {}
cs


사용법 (arguments -> args)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ES5
function sum(){
  var arr = Array.prototype.slice.call(arguments);
  return arr.reduce(function(pre, cur){
    return pre + cur;
  });
}
console.log(sum(1,2,3,4));
 
// ES6
const sum1 = (...args) => {
  return args.reduce((pre, cur) => pre + cur);
}
console.log(sum1(1,2,3,4));
 
cs

 ES5에서 매개변수를 지정하지 않아도 arguments라는 프로퍼티가 함수에 자동으로 생성되어 사용가능 했었으나 화살표 함수에는 arguments대신 args를 사용합니다. 매개변수부분에 rest 파라미터(...)을 사용하여 가변인자 함수 내부에 배열로 전달할 수 있습니다.


자신만의 this를 생성하지 않음(핵심)

화살표 함수는 자신을 포함하는 외부 scope에서 this를 계승받습니다. 즉 화살표 함수는 자신만의 this를 생성하지 않습니다(Lexical this)

화살표 함수의 등장 이전 즉 ES5까지는 모든 새로운 함수를 정의하면 각각의 this가 존재했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person() {
    // Person() 생성자는 `this`를 자신의 인스턴스로 정의.
    console.log(this);
 
    setTimeout(function growUp() {
        // 비엄격 모드에서, growUp() 함수는 `this`를
        // 전역 객체로 정의하고, 이는 Person() 생성자에
        // 정의된 `this`와 다름.
        console.log(this);
    }, 1000);
}
 
var p = new Person();
cs

이 소스에서 this는 window로 바인딩이 되겠죠

setTimeout으로 호출된 growUp 함수의 this객체가 window로 바인딩되어 Person의 this와 growUp의 this는 서로 다른 객체입니다. 이러한 문제를 해결하기 위해 아래와 같이 작성합니다.


1
2
3
4
5
6
7
8
9
10
function Person() {
    console.log(this);
    var that = this
 
    setTimeout(function growUp() {
        console.log(that);
    }, 1000);
}
 
var p = new Person();
cs

1
2
3
4
5
6
7
8
9
function Person() {
    console.log(this);
 
    setTimeout((function growUp() {
        console.log(this);
    }).bind(this), 1000);
}
 
var p = new Person();
cs

this를 변수에 저장해서 해결하거나 bind()함수를 통해 this를 바인딩해서 해결할 수 있습니다. 지금까지 ES5에서 사용하던 방법을 알아보았다면 화살표함수에서는 어떻게 사용하는지 알아보겠습니다.


1
2
3
4
5
6
7
function Person() {
    console.log(this);
 
    setTimeout(() => { console.log(this) }, 1000);
}
 
var p = new Person();
cs


화살표 함수는 자신의 this객체를 생성하지 않기 때문에 위와 같이 사용하지 않아도 this를 사용할 수 있습니다.



반응형
반응형

JavaScript (Scope)


JS는 기본적으로 함수 레벨 스코프를 지원해왔고, 얼마 전까지만 해도 블록 레벨 스코프는 지원하지 않았습니다. 하지만 가장 최신 명세인 ES6(ECMAScript 6)부터 블록 레벨 스코프를 지원하기 시작했습니다.


-> 함수 레벨 스코프

     자바스크립트에서 var로 선언된 변수나, 함수 선언식으로 만들어진 함수는 함수 레벨 스코프를 가집니

     다. 즉, 함수 내부 전체에서 유효하다는 것이죠


1
2
3
4
5
6
7
   function foo() {
           if(true) {
               var color='red';
           }
           console.log(color);        // red
   }
   foo();
cs

   여기서 color가 블록 레벨 스코프라면 if문이 끝날때 사라지고 console.log에서 참조할때 에러가 날것입니다. 하지만 var는 함수 레벨 스코프를 가지고 있기 때문에 foo함수 내부 어디에서도 에러 없이 참조가능한 것이죠.


-> 블록 레벨 스코프

    let 과 const 키워드는 블록 레벨 스코프를 가집니다. 


1
2
3
4
5
6
7
8
   function foo() {
           if(true) {
               let color = 'red';
               console.log(color);        // red
           }
           console.log(color);        // ReferenceError
   }
   foo();
cs


   let color를 if 블록 내부에서 선언하였기 때문에 if문이 끝남과 동시에 잘못된 참조로 에러가 발생하는 것입니다.


그렇다면 let, const, var는 언제 써야 하는 것인가?

요즘은 대부분 var는 사용하지 않습니다. 물론 상황에 맞게 사용하는 것이 맞지만 var는 let과 const로 모두 대체가 가능하고

var 자체가 함수 레벨의 스코프를 가지기 때문에 블록 레벨 스코프보다 더 많은 에러와 혼란은 가져올 수 있습니다.


JavaScript (Closure)


사실 자바스크립트를 공부하면서 많이 어려움을 느낀 부분입니다. 클로저의 정의는 무엇인지도 모르겠고 영어사전에는 닫는것, 종료의 행위 라고 나옵니다.

먼저 정리하면 생성될 당시를 기억하는 것이라고 생각하시면 됩니다. 이는 스코프 체인을 통해 접근할 수 있는 변수 등의 스코프가 해제되었음에도 불구하고 접근할 수 있는 것을 말하는데 아래에서 자세히 살펴보도록 하겠습니다.

자바스크립트에서 클로저는 내부함수가 외부함수의 context에 접근할 수 있는 것을 말합니다. 다음 예시들을 통해 조금 더 자세히 살펴봅시다.


1
2
3
4
5
6
7
8
    function foo() {
        var color = 'red';
        function bar() {
            console.log(color);
        }
        bar();
    }
    foo();
cs

bar 함수는 우리가 부르는 클로저인가요? 일단 bar는 foo안에 속하기 때문에 foo 스코프를 외부 스코프 참조로 가집니다. 그리고 bar는 foo의 color를 참조할 것이죠. 그럼 이 bar 함수는 클로저 일까요? 아닙니다! bar는 foo안에서 정의되고 실행되었을 뿐, foo밖으로 나오지 않았기 때문에 클로저가 아니죠!


그럼 다음 예시를 한번 더 살펴보도록 하죠.


1
2
3
4
5
6
7
8
9
10
    var color = 'red';
    function foo() {
        var color = 'white';
        function bar() {
            console.log(color);
        }
        return bar;
    }
    var a = foo();
    a();
cs


먼저 말씀드리면 이코드는 클로저를 나타내고 있습니다. 

bar는 color를 찾아 출력하는 함수로 정의되었습니다. 또한 bar는 외부함수를 참조로 white를 가리키게 되겠죠. 하지만 foo() 수행이 끝나면 GC가 인스턴스를 회수하지 않나요? 라는 궁금증이 생길 수 있지만 bar()는 a가 여전히 참조하고 있기 때문에 bar에서 참조하는 color는 'white'가 되며 결과값으로 white가 출력됩니다. 정리하면 JavaScript에서 스코프는 소스코드가 작성된 그 문맥에서 결정이 됩니다. 때문에 위 함수에서도 bar()는 계속 참조되고 있기 때문에 GC가 회수하지 않고요.

그래서 white가 출력되는 것입니다.


다른 예시를 통해 다시 한번 살펴보죠.


1
2
3
4
5
6
7
8
9
    function count() {
        var i;
        for(i=1; i<10; i+=1) {
            setTimeout(function timer() {
                console.log(i);
            }, i*100);
        }
    }
    count();
cs


이 코드는 1~9까지를 0.1초마다 출력하는 것이 목표였다고 가정합시다. 하지만 결과값으로는 10이 9번 출력되었습니다. timer는 클로저로 언제 어디서 호출되던지 항상 상위 스코프인 count의 i를 참조하겠죠. 그리고 timer는 0.1초 후 호출됩니다. 그런데 첫 0.1초가 지날동안 이미 i가 10이 되어버린 것입니다. timer는 0.1초 주기로 호출될 때마다 항상 count에서 i를 참조하고 결국 10이 되어버린 i만 출력하는 것입니다.

이 문제를 해결하기 위해서는 어떻게 해야 할까요?


1) 새로운 스코프를 추가하여 반복마다 그곳에 각각 따로 값을 저장하는 방법

2) 블록 스코프를 이용하는 방법


1
2
3
4
5
6
7
8
9
10
11
12
    //1번 방법입니다.
    function count() {
        var i;
        for(i=1; i<10; i+=1) {
            (function(cntNum) {
                setTimeout(function timer() {
                    console.log(cntNum);
                }, i*100);
            })(i);
        }
    }
    count();
cs


1
2
3
4
5
6
7
8
9
//    2번 방법입니다.
    function count() {
        for(let i=1; i<10; i+=1) {
            setTimeout(function timer() {
                console.log(i);
            }, i*100);
        }
    }
    count();
cs


지금까지 자바스크립트의 Scope와 Clouser에 대해 알아보았습니다. 클로저라는 개념자체가 굉장히 머리아프고 까다롭게 느껴지지만 이 글이 도움이 되었으면 합니다. 혹시라도 더 깊게 알고 싶으신 분들은 자바스크립트 렉시컬 스코프에 대해 학습하시면 도움이 되실 것입니다.

반응형

'JavaScript' 카테고리의 다른 글

자바스크립트 화살표 함수  (0) 2019.02.26
[JavaScript] JS의 장점?? JS의 좋은 문법  (0) 2018.09.20
반응형

javascript를 좋아하는 입장에서 더욱 애정이 가는 글을 쓰게 되네요. 왜 좋냐고 물어보시면 다됩니다. 이게 될까? 하면 되고 이것도? 하면 되는게 javascript이기 때문이죠. (물론 제가 아직 내공이 많이 부족해서 그럴 수도 있습니다) 또한, 프로그래밍 언어 대부분은 데이터 타입에 대해 강력하게 요구하죠. 하지만 javascirpt는 느슨한 타입 체크를 합니다. 물론 컴파일러가 오류를 찾을 수 없다는 단점으로 작용하기도 하죠. 하지만 이는 오류를 찾을 수 없지만 그만큼 자유로움을 준다는 이야기이기도 합니다! 그리고 점점 javascript가 중요시 되고 있는 이 시점에서 싫어한다면 개발자로 손해 아닐까요?? 여담은 이쯤하고 이제 본론으로 넘어가도록 하겠습니다.


모든 코드는 Atom 에디터, Chrome, Mac OS환경에서 진행하겠습니다.


자바스크립트 기본서를 읽으셨다면 다양한 문법들을 배우셨을겁니다. 저는 이중 크게 자바스크립트에서 (감히)쓸만한? 좋은! 문법들을 말해보고자 합니다.


1) 숫자

2) 문자열(string)

3) 문장(statements)

4) 표현식(Expression)

5) 리터럴

6) 함수


첫 번째는 숫자입니다. 자바스크립트에서는 정수와 실수의 구분이 없습니다. 즉 1과 1.0이 같은 값입니다. 이는 오버플로우를 피할 수 있는 편리한 특성이라고 할 수 있겠죠. 


두 번째는 문자열입니다. 자바스크립트에서는 숫자와 같이 문자 타입이 없습니다. 또한 length라는 속성도 존재하며 문자열이 한번 만들어지면 불변하는 특성도 가지고 있죠. 


세 번째는 문장입니다. case, while, if, try, throw, break등등... 이러한 기능이 없다면 언어라고 생각할 수도 없이 굉장히 유용하고 자주 사용하는 기능이죠.


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


사실 지금까지는 뭐야? 말장난하는건가? 라고 생각하실수도 있습니다. ㅎㅎ 사실 문법에서 좋은 문법이 뭐 자주사용하고 유용하기 때문에 더 이렇게 작성할 수도 있겠네요


네 번째로는 표현식인데 이는 특히 삼항 연산자를 말씀드리고 싶네요. 현업에서도 많이 사용하고 개인적으로는 알고리즘 풀이를 할때도 자주 사용하는 연산자입니다. 자바스크립트만의 특징이라고 할 수는 없지만 굉장히 편리한 건 사실입니다. 또한 typeof, 논리연산자 등 다양한 표현식을 제공합니다.


다섯 번째는 리터럴입니다. 객체 리터럴은 새로운 객체를 생성할 때 편리한 표기법입니다. 속성명은 이름 또는 문자열로 지정할 수 있습니다. 리터럴은 고정된 값을 뜻하는데 어떠한 값을 명칭하는 것이 아니라 저장된 값 자체를 말하는 것이다. 때문에 정수 리터럴, 문자열 리터럴, 배열 리터럴 다양한 리터럴이 존재하고 불린다. 즉, 리터럴이란 상수와 마찬가지로 메모리 어딘가에 값이 변하지 않도록 저장이 되지만 그 이름은 없는 것이다. 다시 말해 컴파일시 프로그램 내에 정의되어 있는 그대로 해석되어야 하는 값이라고 할 수 있다. 자세한 내용은 따로 리터럴에 대해 다룰 예정이니 조금만 참아주세요!! ㅎㅎ 궁금하시면 개발자는 go google!


마지막으로는 함수 입니다. 이 함수도 함수 리터럴로 사용할 수 있는데 함수 리터럴은 함수값을 정의합니다. 또한 자바스크립트에서의 함수도 우리가 기존에 사용했던 언어와 굉장히 유사하다고 볼 수 있겠네요!


그럼 다음 장부터는 자바스크립트를 본격적으로 공부해보도록 하겠습니다. 

반응형

+ Recent posts