11. 화살표 함수

양희준·2022년 1월 2일
0

JavaScript Info

목록 보기
11/19
post-thumbnail

📌 11-1 화살표 함수란?

💡 ES6 (ECMA2015)에서 추가된 내용이다.
💡 ES6에서 화살표함수와 class의 메소드 축약표현으로 인해 함수의 사용목적에 따라 표현을 다르게 사용할 수 있다.
💡 변수에 담아서 사용하기 때문에 함수 호이스팅이 발생하지 않는다.

📌 11-2 함수 구분하기

함수에는 선언방식과 호출방식에 따라서 구분된다.

💡 async, 제너레이터, 즉시실행함수는 제외하고 작성했다.

// 함수 선언문 (ES6 이전)
function func1() {
    console.log("func1");
}
// 함수 표현식 (ES6 이전)
const func2 = function() {
    console.log("func2");
}
// ES6 화살표 함수
const func3 = () => {
    console.log("func3");
}
// ES6 메소드 축약 표현
const obj = {
    func4() {
        console.log("func4")
    }
}
// 생성자 함수
function Func() {
    this.name = "Func";
    console.log(this.name);
}
// 일반 함수로서 호출
func1();
func2();
func3();
// 메소드로서 호출
obj.func4();
// 생성자 함수로서 호출
new Func();

📃 생성자 함수는 일반 함수 선언문만 같지만 호출방식에 따라서 달라진다.

📌 11-3 화살표 함수의 사용방법

1. 함수 선언문 대신해서 사용하기.

// () 안에 파라미터를 넣어주고 {} 안에 실행문과 반환값이 있다면 return을 작성한다.
const add = (num1, num2) => {
    return num1 + num2;
}
// 결과 : 3
console.log(add(1,2));

2. 실행문이 없고 바로 반환값만 있을 경우 축약표현

// 실행문 없이 반환값이 필요할 때에는 {}를 생략한다. {return num1 + num2}와 같은표현
const add = (num1, num2) => num1 + num2;
// 결과 : 3
console.log(add(1,2));

3. 실행문이 없고 파라미터가 한개만 있을 경우

// 파라미터가 하나일 경우에는 ()도 생략 가능하다.
const add = num => num + 2;
// 결과 : 3
console.log(add(1));

4. CallBack 파라미터로 사용하는 경우

const num = [1,2,3];
// 파라미터를 () => {} 즉 화살표 함수로 넘기는것도 가능하다.
const newNum = num.map((item) => {
    return item + 1;
});
// 결과 : [2,3,4] 출력
console.log(newNum);

📌 11-4 ES6 이후 함수의 구분

함수의 구분constructorprototypesuper
일반 함수OOO
메소드XXO
화살표 함수XXX

1. constructor가 없으면 생성자 함수로는 사용이 불가능하다.

// 일반적인 생성자 함수를 생성하여 인스턴스를 생성 방법
const Animals = function(name) {
    this.name = name;
}
Animals.prototype.log = function() {
    console.log(this.name);
}
const animals = new Animals("Dog");
animals.log();
// constructor 프로퍼티가 존재하지 않기 때문에 에러 발생
const Animals = (name) => {
    this.name = name;
}
Animals.prototype.log = function() {
    console.log(this.name);
}
const animals = new Animals("Dog");
animals.log();

2. super가 없으면 super를 사용이 불가능하다.

// 일반적인 상속구조
class App {
    log() {
        console.log("App");
    }
}
class Animal extends App {
    constructor() {
        super();
        super.log();
    }   
}
new Animal();
// super가 없기 때문에 super 키워드를 사용하면 에러 발생.
class App {
    log = () => {
        console.log("App");
    }
}
class Animal extends App {
    constructor() {
        super();
        super.log();
    }   
}
new Animal();

📃 ES6이후로 함수를 구분하는 이유는 시멘틱적 이유가 있기 때문이라고 생각한다. 생성자 함수를 만들꺼면 일반함수를 호출하여 생성자 함수를 만들고 상속을 이용하기 위해서는 메소드 축약 표현을 이용하며 일반함수로 사용할 경우에는 화살표 함수를 사용하여 시멘틱적으로 알아보기 쉽게 하기위해서 구분하는것 같다.

📌 11-5 화살표 함수의 this 참조 (객체)

화살표 함수는 생성된 시점의 화살표 함수가 아닌 상위 스코프의 this를 가지게 된다.

💡 화살표 함수를 사용하지 않는 경우

// 에러 발생
const colors = {
    colorName: ["Red", "Blue", "Green"],
    log() {
        this.colorName.forEach(function(item) {
            // 타입 에러발생 this.colorName.length를 찾을 수 없음
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`)
        });
    }
}
colors.log();

this는 호출되는 방법으로 this가 정해진다. colors 객체 안에 log 메소드에서 this는 호출되는 객체인 colors를 가리킨다. 하지만 log 메소드 안에있는 forEach메소드안에 콜백함수는 함수로 호출이 되어있기 때문에 함수 호출의 this를 가리키는 것은 window 객체를 가리킨다. 그래서 this를 잃어버리게 된다.

// Node.js 실행환경 : globalThis.colorName = ["Red", "Blue", "Green"];
// window 객체에 임의로 넣어주기
window.colorName = ["Red", "Blue", "Green"]; 
const colors = {
    colorName: ["Red", "Blue", "Green"],
    log() {
        this.colorName.forEach(function(item) {
            /*
            정상 출력
            색깔: Red / 총 개수: 3
            색깔: Blue / 총 개수: 3
            색깔: Green / 총 개수: 3
            */
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`)
        });
    }
}
colors.log();

HTML로 열지 않고 Node로 실행하면 window 객체를 사용하지 못함으로 globalThis에 넣어준다.

const colors = {
    colorName: ["Red", "Blue", "Green"],
    log() {
        const callBack = function(item) {
            /*
            정상 출력
            색깔: Red / 총 개수: 3
            색깔: Blue / 총 개수: 3
            색깔: Green / 총 개수: 3
            */
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`);
        }
        // bind를 교체한다 지금 log 메소드 안에서 this는 객체인 colors를 가리킨다.
        const bindCallBack = callBack.bind(this);
        this.colorName.forEach(bindCallBack);
    }
}
colors.log();

Function.prototype.bind 메소드를 사용하여 this를 교체하면 해결된다.

💡 화살표 함수를 사용한 경우

const colors = {
    colorName: ["Red", "Blue", "Green"],
    log() {
        this.colorName.forEach((item) => {
            /*
            정상 출력
            색깔: Red / 총 개수: 3
            색깔: Blue / 총 개수: 3
            색깔: Green / 총 개수: 3
            */
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`);
        });
    }
}
colors.log();

📃 ES6의 화살표 함수를 사용하면 해결이 말끔하게 된다 이유는 forEach 메소드안에서 생성된 화살표 함수는 생성된 시점의 화살표함수가 아닌 this를 가지게 된다. 그러므로 지금 this는 log 메소드의에서 this를 참조한다. log는 메소드로 사용됨으로 메소드는 호출할시 메소드의 this 영역은 객체를 가리킨다. 그러므로 에러가 나오지 않고 잘 찾아낸다.

📌 11-5 화살표 함수의 this 참조 (class)

💡 class에서 화살표 함수를 사용하지 않았을 경우

// 객체와 마찬가지로 에러 발생
class Colors {
    constructor(colorName) {
        this.colorName = colorName;
    }
    log() {
        this.colorName.forEach(function(item) {
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`)
        });
    }
}
const colors = new Colors(["Red", "Blue", "Yellow"]);
colors.log();

📃 클래스로 인스턴스를 생성하였을 경우 메소드는 인스턴스를 가리키게 된다. 그러므로 위와 같은 맥락으로 에러가 발생된다.

🧩 Function.prototype.bind 메소드를 사용하여 this를 교체하면 해결된다.

class Colors {
    constructor(colorName) {
        this.colorName = colorName;
        // 콜백함수에 bind를 해준다
        this.callback = this.callback.bind(this);
    }
    callback(item) {
        console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`);
    }
    log() {
        this.colorName.forEach(this.callback);
    }
}
const colors = new Colors(["Red", "Blue", "Yellow"]);
/*
정상 출력
색깔: Red / 총 개수: 3
색깔: Blue / 총 개수: 3
색깔: Yellow / 총 개수: 3
*/
colors.log();

💡 class에서 화살표 함수를 사용했을 경우

class Colors {
    constructor(colorName) {
        this.colorName = colorName;
    }
    log() {
        this.colorName.forEach((item) => {
            console.log(`색깔: ${item} / 총 개수: ${this.colorName.length}`);
        });
    }
}
const colors = new Colors(["Red", "Blue", "Yellow"]);
/*
정상 출력
색깔: Red / 총 개수: 3
색깔: Blue / 총 개수: 3
색깔: Yellow / 총 개수: 3
*/
colors.log();

📌 11-6 화살표 함수와 Rest Parms

화살표 함수는 arguments가 존재하지 않는다.

일반 함수에서는 유동적인 파라미터들을 받을 경우 arguments을 이용해서 해결했다.

const showNum = function() {
    const params = arguments;
    for(let i = 0; i < params.length; i++) {
        console.log(arguments[i]);
    }
}
// 출력 : 1, 2, 3, 4
showNum(1,2,3,4);

화살표 함수에는 arguments가 없어서 에러가 발생한다.

const showNum = () => {
    const params = arguments;
    // arguments가 찾을 수 없다고 에러 출력
    console.log(params);
}
showNum(1,2,3,4);

해결방안은 Rest operator를 사용한다.

// ...rest를 인자로 넘겨준다.
const showNum = (...rest) => {
    const params = rest;
    for(let i = 0; i < params.length; i++) {
        console.log(params[i]);
    }
}
// 출력 : 1, 2, 3, 4
showNum(1,2,3,4);

📌 11-7 React 회고록

React에서의 this.bind의 이유

import React, { Component } from 'react';

class App extends Component {
    constructor(props) {
        super(props);
        // 함수 안에서 호출하기 때문에 Window를 참조하여 state를 참조 못하게 된다.
        // 그러므로 this를 bind 해준다.
        this.increaseCount = this.increaseCount.bind(this);
        this.decreaseCount = this.decreaseCount.bind(this);
        this.state = {
            count: 0
        }
    }
    increaseCount() {
        this.setState({ count: this.state.count + 1 });
    }
    decreaseCount() {
        this.setState({ count : this.state.count - 1});
    }
    render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.increaseCount}>증가</button>
                <button onClick={this.decreaseCount}>감소</button>
            </div>
        );
    }
}

export default App;
import React, { Component } from 'react';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    // 화살표 함수로 this는 class App를 가리키게 된다.
    increaseCount = () => {
        this.setState({ count: this.state.count + 1 });
    }
    decreaseCount = () => {
        this.setState({ count : this.state.count - 1});
    }
    render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.increaseCount}>증가</button>
                <button onClick={this.decreaseCount}>감소</button>
            </div>
        );
    }
}

export default App;

📃 옛날에 React를 처음 사용하였을 때는 Hook말고 Component를 사용하였는데 그때 이 문제를 직면하고 그냥 화살표 함수만 사용하였다. 근데 원리를 알게되어서 신기하다.

profile
JS 코린이

0개의 댓글