본문 바로가기

Front End/Javascript

[JavaScript] 화살표 함수(Arrow Function)

전통적인 자바스크립트에서 함수를 만들 때는 function 키워드를 사용하여 정의할 수 있습니다.  
예를들어 두 수의 합을 구하는 함수는 다음과 같이 작성할 수 있습니다.

          
        function add1(x, y){
            return x + y;
        }

        let add2 = function(x, y){
            return x + y;
        }

 

ES6부터는 화살표 함수(arrow function)라는 새로운 문법 방식으로 함수를 만들 수 있게 되었습니다. 화살표 함수는  => 기호를 사용하여 function 키워드를 생략할 수 있습니다.
function f(){} 를 const f = () => {};게 표현이 가능합니다.

단순한 리턴문만 있다면 한줄로 더 심플하게 줄일 수 있습니다.


        let add3 = (x, y) => {
            return x + y;
        }

        // 단순한 리턴문만 있다면 한줄로 더 심플하게 줄일 수 있다
        let add4 = (x, y) => x+y;

 

 

arrow_function_ex001.html 파일을 다음과 같이 만듭니다.

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
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>화살표 함수 (Arrow function)</title>
    <script>
        function add1(x, y){
            return x + y;
        }
 
        let add2 = function(x, y){
            return x + y;
        }
 
        let add3 = (x, y) => {
            return x + y;
        }
 
        let add4 = (x, y) => x+y;
 
        window.onload = ()=>{
            document.querySelector("#root").innerHTML += add1(5,6+ "<br/>";
            document.querySelector("#root").innerHTML += add2(5,6+ "<br/>";
            document.querySelector("#root").innerHTML += add3(5,6+ "<br/>";
            document.querySelector("#root").innerHTML += add4(5,6+ "<br/>";
        }
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>
cs

 

실행 결과 입니다.

 

매개변수의 사용

매개변수가 없을 경우 빈괄호를 사용합니다.

함수 몸체 내의 코드가 한줄이고 단순히 return문 밖에 없다면 중괄호와 return 키워드를 생략할 수 있습니다.

let say1 = ()=> `환영합니다.<br>`;

 

매개변수가 한 개인 경우, 소괄호를 생략할 수 있으나 매개변수가 여러 개인 경우, 소괄호를 생략할 수 없습니다.

let say2 = name => `${name}님 환영합니다.<br>`;
let say3 = (name=> `${name}님 환영합니다.<br>`;

 

함수 내용이 여러줄이면 {}을  사용하고 return값이 있을 경우 return을 사용합니다.

let say4 = (name=> {
name = `<b style='color:blue'>★${name}★</b>`;    
return `${name}님 환영합니다.<br>`;
}

 

매개변수로 함수를 전달할 수 있습니다.

let add = (x, y) => x+y;
let sub = (x, y) => x-y;
let mul = (x, y) => x*y;
let div = (x, y) => x/y;
let calc = (x, y, operator, callback)=> `${x} ${operator} ${y} =  ${callback(x,y)} <br>`;
calc(53'+', add)

 

arrow_function_ex002.html 파일을 다음과 같이 만듭니다.

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
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>화살표 함수 (Arrow function)</title>
    <script>
        let say1 = ()=> `환영합니다.<br>`;
        let say2 = name => `${name}님 환영합니다.<br>`;
        let say3 = (name=> `${name}님 환영합니다.<br>`;
        let say4 = (name=> {
            name = `<b style='color:blue'>★${name}★</b>`;    
            return `${name}님 환영합니다.<br>`;
        }
 
        let add = (x, y) => x+y;
        let sub = (x, y) => x-y;
        let mul = (x, y) => x*y;
        let div = (x, y) => x/y;
        let calc = (x, y, operator, callback)=> `${x} ${operator} ${y} =  ${callback(x,y)} <br>`;
 
        window.onload = ()=>{
            document.querySelector("#root").innerHTML += say1();
            document.querySelector("#root").innerHTML += say2('한사람');
            document.querySelector("#root").innerHTML += say3('한사람');
            document.querySelector("#root").innerHTML += say4('한사람');
            document.querySelector("#root").innerHTML += calc(53'+', add);
            document.querySelector("#root").innerHTML += calc(53'-', sub);
            document.querySelector("#root").innerHTML += calc(53'*', mul);
            document.querySelector("#root").innerHTML += calc(53'/', div);
        }
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>
cs

 

실행 결과 입니다.

 

객체반환 / 매개변수 기본값 / 구조분해 할당 / 매개변수들

객체 리터럴을 반환하려면 소괄호로 감싸야 한다. 
왜냐하면 딸랑 중괄호 { } 쓰면 얘가 함수 블록인지 객체 블록인지 판단할수 없기 때문이다.

        let getObj1 = ()=>{
            return {result : 100};
        }

        let getObj2 = ()=>({result : 100})

 

매개변수에 기본값을 지정할 수 있습니다.

        let add1 = (a = 1, b = 2) => a + b;

 

기본값을 구조 분해 할당할 수 있습니다. 반드시 호출시에 매개변수 개수를 맞춰야 합니다.

        let add2 = ([a, b] = [11, 22]) => a + b;

 

매개변수들을 배열로 받을 수 있습니다.

        let paramView = (...args) => `타입(${typeof(args)}) : ${args}`;
        let doubleView = (...args) => args.map((n)=>2*n);

 

arrow_function_ex003.html 파일을 다음과 같이 만듭니다.

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
48
49
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>화살표 함수 (Arrow function)</title>
    <script>
        // 객체 리터럴 반환
        // 객체 리터럴을 반환하려면 소괄호로 감싸야 한다. 
        // 왜냐하면 딸랑 중괄호 { } 쓰면 얘가 함수 블록인지 객체 블록인지 판단할수 없기 때문이다.
        let getObj1 = ()=>{
            return {result : 100};
        }
 
        let getObj2 = ()=>({result : 100})
 
        // 매개변수 기본값
        let add1 = (a = 1, b = 2=> a + b; 
        // 구조 분해 할당
        let add2 = ([a, b] = [1122]) => a + b; 
        // 나머지 매개변수
        let paramView = (...args) => `타입(${typeof(args)}) : ${args}`;
        let doubleView = (...args) => args.map((n)=>2*n);
 
        let repeat = (data, count) => (data+'').repeat(count);
 
        window.onload = () => {
            document.querySelector("#root").innerHTML += JSON.stringify(getObj1()) + "<br>";    
            document.querySelector("#root").innerHTML += JSON.stringify(getObj2()) + "<br>";    
            document.querySelector("#root").innerHTML += `결과 : ${getObj2().result} <br><hr>`;    
            document.querySelector("#root").innerHTML += `add1 결과1 : ${add1(1013)} <br>`;    
            document.querySelector("#root").innerHTML += `add1 결과2 : ${add1(10)} <br>`;    
            document.querySelector("#root").innerHTML += `add1 결과3 : ${add1()} <br><hr>`;    
            document.querySelector("#root").innerHTML += `add2 결과1 : ${add2([1013])} <br>`;    
            document.querySelector("#root").innerHTML += `add2 결과2 : ${add2([1])} <br>`;    // 보낼거면 2개
            document.querySelector("#root").innerHTML += `add2 결과3 : ${add2()} <br><hr>`;    
            document.querySelector("#root").innerHTML += `paramView 결과1 : ${paramView(1234)} <br>`;    
            document.querySelector("#root").innerHTML += `paramView 결과2 : ${paramView('하나','둘','셋')} <br>`;    
            document.querySelector("#root").innerHTML += `doubleView 결과 : ${doubleView(11223344)} <br>`;    
 
            document.querySelector("#root").innerHTML += `repeat 결과 : ${repeat(510)} <br>`;    
            document.querySelector("#root").innerHTML += `repeat 결과 : ${repeat('만세 '3)}!!! <br>`;    
        }
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>
cs

 

실행 결과 입니다.

 

 

1급시민(first class citizen) 혹인 1급멤버(first class member) 개념

함수가 1급시민이 되려면  변수에 넣을수 있고, 함수의 인수로 넘길수 있고, 함수의  반환에도 함수를 사용할 수 있음을 말합니다. 즉, 일반 자료형과 같이 함수를 처리할 수 있다는 뜻입니다.

자바스크립트에서 함수는 1급시민입니다.

 

 

arrow_function_ex004.html 파일을 다음과 같이 만듭니다.

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="ko">
 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>화살표 함수 (Arrow function)</title>
    <script>
        window.onload = () => {
            // 매개변수로 함수를 받고 함수를 리턴받아 바로 실행합니다.
            ((prompt("나이?."18< 18) ? () => alert('안녕 얼라는 가라!!!') : () => alert("안녕하세요! 놀다 가십시오"))();
 
            // 1. 변수에 함수 대입
            let fn1 = function () {
                return `함수를 변수에 대입가능하다.`;
            }
            let fn2 = () => `함수를 변수에 대입가능하다.`;
 
            console.log(fn1());
            console.log(fn2());
            console.log((() => `나는 즉시 실행 함수 입니다.`)());
 
            // 2. 함수를 객체 멤버로
            let object = {
                "message""함수를 객체 멤버로 가질수 있습니다.",
                view() {
                    return this.message;
                },
                fn1, fn2
            }
            console.log(object.view())
            console.log(object.fn1())
            console.log(object.fn2())
 
            // 3. 함수를 배열 멤버로
            let array = [() => "함수를 배열 멤버로가빕니다.", fn1, fn2];
            console.log(array)
            console.log(array[0]())
            console.log(array[2]())
 
            // 4. 함수를 인수로 받기
            let five_call = (fn) => {
                for (let i = 0; i < 5; i++console.log(fn());
            }
 
            five_call(fn1);
            five_call(fn2);
 
            // 5. 함수를 리턴
            let makeShowMessage = (decorator) => {
                return (message) => {
                    console.log(`${decorator} ${message} ${decorator}`);
                }
            }
            let starMeaage = makeShowMessage('★');
            starMeaage("꿈은 이루어진다!!!");
            starMeaage("꿈은 이루어진다~~~");
            makeShowMessage('♣')("꿈은 이루어진다.");
            makeShowMessage('♥')("꿈은 이루어진다.");
        }
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>
cs

 

실행 결과 입니다.

 

 

 

화살표 함수의 특징

자바스크립트의 화살표 함수는 function 함수를 축약하여 표현하는 문법이지만, 일반적인 함수와 다른 몇 가지 고유한 특징을 가집니다.  
이러한 특징은 화살표 함수를 유용하게 만들기도 하지만, 함정이 되기도 하기 때문에 유의깊게 살펴보아야 합니다.

화살표 함수엔 this가 없습니다.
우선 화살표 함수에는 this가 없습니다. 그래서 화살표 함수에서 this 키워드로 접근하면, 자신이 아닌 외부에서 값을 가져오게 됩니다.

예를들어 다음 user 라는 객체의 sayHi의 프로퍼티가 어떠한 형태의 함수로 이루어져 있느냐에 따라 this.name 이 가리키는 값이 달라지게 됩니다.  
화살표 함수는 자신만의 this를 가지지 않기 때문에, 자신을 감싸는 외부 환경의 this를 그대로 따르기 때문입니다. 
즉, 화살표 함수에서 this는 정적으로 결정됩니다.

        var name = "전역(window 영역)";

        let user1 = {
            name: "지역(user1 영역)",
            sayHi1: function() { // 일반 함수
                console.log(this.name);
                return this.name;
            },
            sayHi2: () => { // 화살표 함수
                console.log(this.name);
                return this.name;
            },
        };

       
        user1.sayHi1();  // 지역(user1 영역)
        user1.sayHi2();  // 전역(window 영역)

 

        let studentList = {
            class : '1반',
            students : "한놈,두식이,석삼,너구리,오징어".split(","),
            viewList1 : () =>{
                let result = "<hr>";
                studentList.students.forEach(function(name){
                    result += `${this.class} : ${name}  <br/>`; // this ==== window
                });
                return result + "<hr>";
            },
            viewList2(){
                let result = "";
                studentList.students.forEach((name) => {
                    result += `${this.class} : ${name}  <br/>`; // this ==== studentList
                });
                return result + "<hr>";
            }
        }

       
       studentList.viewList1()
       // 결과  
       undefined : 한놈
       undefined : 두식이
       undefined : 석삼
       undefined : 너구리
       undefined : 오징어

       studentList.viewList2()
        // 결과
       1반 : 한놈
       1반 : 두식이
       1반 : 석삼
       1반 : 너구리
       1반 : 오징어


살표 함수엔 arguments 가 없습니다.
자바스크립트 함수의  arguments는 일반적인 함수가 호출될 때 전달된 인수들을 담고 있는 유사 배열 객체입니다.

        function argsFunc1() {
            console.log(`모든 인수를 받는 유사 배열 객체 : ${JSON.stringify(arguments)}`);
        }
       
        let argsFunc2 = ()=> {
            // 에러 발생 : Uncaught ReferenceError: arguments is not defined
            // console.log(`모든 인수를 받는 배열 : ${arguments}`);
        }

        let argsFunc3 = (...args)=> {
            console.log(`모든 인수를 받는 배열 : ${args}`);
        }


         argsFunc1(1, 2, 3); // 모든 인수를 받는 유사 배열 객체 : {"0":1,"1":2,"2":3}
         argsFunc2(1, 2, 3);
         argsFunc3(1, 2, 3); // 모든 인수를 받는 배열 : 1,2,3


화살표 함수는 생성자 함수가 없습니다.
생성자 함수란 new 연산자와 함께 호출되어 객체를 생성하는 함수를 말합니다.
역시 화살표 함수는 this가 없기 때문에 new와 함께 실행할 수 없습니다.

따라서 화살표 함수는 new와 함께 호출할 수 없습니다.

따라서 화살표 함수는 객체를 생성하는 용도로 사용할 수 없습니다.
그래서 화살표 함수는 보통 콜백 함수나 익명 함수로서 사용되는 편입니다.
추가로 화살표 함수는 클래스 관련 키워드인 super 도 없습니다.

        function MyUser1(name, age, gender) {
            this.name = name;
            this.age = age;
            this.gender = gender;
        }
        let MyUser2 = (name, age, gender) => {
            this.name = name;
            this.age = age;
            this.gender = gender;
        }


         let myuser1 = new MyUser1('한사람', 22, true);
         console.log(myuser1);
         Uncaught TypeError: MyUser2 is not a constructor
         let myuser2 = new MyUser2('두사람', 18, false);
         console.log(myuser2);


화살표 함수를 남용해서는 안되는 경우
살펴본 바와 같이 화살표 함수는 일반 함수와는 달리 자신만의 this를 가지지 않습니다.
그래서 화살표 함수를 자바스크립트에서 사용함에 있어 몇가지 제약이 생깁니다.

 

1. 일반 객체의 메소드 사용 못합니다.  (this가 없으니까)
2. prototype 메소드로 사용 못합니다.  (마찬가지 프로토타입 객체 this참조 불가능)
3. 생성자 함수로 사용 못합니다.   (화살표 함수는 prototype 프로퍼티를 가지고 있지 않다)
4. addEventListener 함수의 콜백함수는 조심해서 사용해야 합니다.
5. 화살표 함수는 call, apply, bind 메소드를 사용하여 this를 변경할 수 없습니다.

 

 

arrow_function_ex005.html 파일을 다음과 같이 만듭니다.

 

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<!DOCTYPE html>
<html lang="ko">
 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>화살표 함수 (Arrow function)</title>
    <script>
        var name = "전역(window 영역)";
 
        let user1 = {
            name"지역(user1 영역)",
            sayHi1: function() { // 일반 함수
                console.log(this.name); 
                return this.name;
            },
            sayHi2: () => { // 화살표 함수
                console.log(this.name); 
                return this.name;
            },
        };
 
        let studentList = {
            class : '1반',
            students : "한놈,두식이,석삼,너구리,오징어".split(","),
            viewList1(){
                let result = "<hr>";
                studentList.students.forEach(function(name){
                    result += `${this.class} : ${name}  <br/>`; // this ==== window
                });
                return result + "<hr>";
            },
            viewList2(){
                let result = "";
                studentList.students.forEach((name=> {
                    result += `${this.class} : ${name}  <br/>`; // this ==== studentList
                });
                return result + "<hr>";
            }
        }
 
        function argsFunc1() {
            console.log(`모든 인수를 받는 유사 배열 객체 : ${JSON.stringify(arguments)}`);
        }
        
        let argsFunc2 = ()=> {
            // 에러 발생 : Uncaught ReferenceError: arguments is not defined
            // console.log(`모든 인수를 받는 배열 : ${arguments}`);
        }
 
        let argsFunc3 = (...args)=> {
            console.log(`모든 인수를 받는 배열 : ${args}`);
        }
 
        function MyUser1(name, age, gender) {
            this.name = name;
            this.age = age;
            this.gender = gender;
        }
        let MyUser2 = (name, age, gender) => {
            this.name = name;
            this.age = age;
            this.gender = gender;
        }
 
        window.onload = () => {
            document.querySelector("#root").innerHTML += `${user1.sayHi1()}<br>`;    // 지역(user1 영역)
            document.querySelector("#root").innerHTML += `${user1.sayHi2()}<br>`;    // 전역(window 영역
            document.querySelector("#root").innerHTML += `${studentList.viewList1()}<br>`; 
            document.querySelector("#root").innerHTML += `${studentList.viewList2()}<br>`; 
 
            argsFunc1(123);
            argsFunc2(123);
            argsFunc3(123);
 
            let myuser1 = new MyUser1('한사람'22true);
            console.log(myuser1);
            // Uncaught TypeError: MyUser2 is not a constructor
            // let myuser2 = new MyUser2('두사람', 18, false);
            // console.log(myuser2);
        }
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>
cs

 

실행 결과 입니다.