티스토리 뷰

front-end/JavaScript

Learning JavaScript 함수 ES6

이안_ian 2019. 11. 14. 22:03




반응형

자바스크립트에서의 함수

자바스크립트에서는 함수도 객체입니다. 따라서 다른 객체와 마찬가지로 넘기거나 할당 할 수 있습니다. 함수 호출과 참조의 차이를 이해하는 것이 중요합니다. 함수 식별자 뒤에 괄호를 쓰면 자바스크립트는 함수를 호출하려 한다고 이해하고, 함수 바디를 실행합니다. 그리고 함수를 호출한 표현식은 반환값이 됩니다. 괄호를 쓰지 않으면 다른 값과 마찬가지로 함수를 참조하는 것이며 그 함수는 실행되지 않습니다.

function getGreeting() {
   return "Hello World";
}

getGreeting();	//"Hello World"
getGreeting;	//function getGreeting()

함수를 호출하지 않고 다른 값과 마찬가지로 참조하기만 할 수 있다는 특징은 자바스크립트를 매우 유연한 언어로 만듭니다. 예를 들어 함수를 변수에 할당하면 다른 이름으로 함수를 호출 할 수 있습니다.

const f = getGreeting;
f();	//"Hello World"

함수를 객체 프로퍼티에 할당할 수도 있고 배열에도 할당 가능합니다.

const o = {};
o.f = getGreeting;
o.f();	//"Hello World"

const arr = [1,2,3];
arr[1] = getGreeting;
arr[1](); //"Hello World"

함수도 객체이므로 매개변수로 함수를 넘기면 주소값을 던지게 되는 것이라 값을 바꿀 수 있습니다. 하지만 좀더 명확히 히 말해야 할 점이 있습니다. 함수 안의 값(o)과 함수 밖의 개체는 서로 다른 개체입니다. 하지만 그 둘은 같은 객체를 가리키고 있습니다. 아래 예제를 통해 확인해 봅시다.

function f(o){
    o.msg = "f에서 수정함";

    o = {
        msg : "새로운 객체"
    };
    console.log(`f 내부: o.msg = "${o.msg}" 할당 후`);
}

let o = {
    msg : "초기값"
}

console.log(`f를 호출하기 전: o.msg = "${o.msg}"`);
f(o);
console.log(`f를 호출한 다음: o.msg = "${o.msg}"`);

이 예제의 핵심은 함수 내부의 매개변수 o와 함수 바깥의 변수 o가 다르다는 겁니다. f를 호출하면 둘은 같은 객체를 가리키지만, f 내부에서 o에 할당한 객체는 새로운, 완전히 다른 객체 입니다. 함수 바깥의 o는 여전히 원래 객체를 가리키고 있습니다.

매개변수 선언이 필요없는 JS의 함수

자바같은 언어에는 오버로딩이라는 개념이 존재합니다. 오버로딩은 함수 이름이 같고 매개변수만 달라지는 것에 같은 함수 이름을 쓰도록 하기 위함입니다. 그리고 매개변수를 타입에 맞는 갯수를 꼭 같이 넘겨줘야하죠.

함수이름은 같지만 매개변수가 달라진다 => 오버로딩

 

하지만 자바스크립트는 매개변수를 선언했어도 그 값을 안던져도 됩니다. 그러면 그 매개변수는 undefined가 될뿐 에러가 나지 않아요! 심지어 매개변수를 1개만 선언해도 더 많은 매개변수를 던져도 그 값을 받을 수 있죠

객체의 프로퍼티인 함수

객체의 프로퍼티인 함수를 메서드라고 불러서 일반적인 함수와 구별합니다. 객체 리터럴에서도 메서드를 추가할 수 있습니다.

const o = {
  name: "Wallace",
  bark: function() {return "Woof!";},
}

//좀더 간편한 방법
const o = {
  name: "Wallace",
  bark: {return "Woof!"},
}

this 키워드

자바스크립트안에서 this는 우리가 알던 방식과는 조금 다르게 동작합니다. this는 함수를 어떻게 선언했느냐가 아니라 어떻게 `호출` 했느냐에 따라 달라집니다.

const o = {
  name: 'Wallace',
  speak() { return "My name is ${this.name}`; },
}

o.speak();			//"My name is Wallace

const speak = o.speak;
speak === o.speak;		//true; 두 변수는 같은 함수를 가리킵니다.
speak();			//"My name is undefined"

처음에는 o에서 speak를 호출 했기 때문에 this.name에 Wallac를 찾을 수 있었지만 함수를 변수에 할당하고 호출하면 자바스크립트는 이 함수가 어디에 속해 있는지 알 수 없으므로 this는 undefined에 묶입니다.

 

중첩된 함수 안에서 this를 사용하려다 보면 혼란스러울 때가 많습니다.

const q = {
    name: "Julie",
    greetBackwards: function(){
        function getReverseName(){
            let nameBackwards = '';
            for(let i=this.name.length-1; i>=0;i--){
                nameBackwards += this.name[i];
            }
            return nameBackwards;
        }
        return console.log(`${getReverseName()} si eman ym, ollH`);
    },
};
q.greetBackwards();

const w = {
    name: "Julie",
    greetBackwards: function(){
        const self = this;
        console.log(self);
        function getReverseName(){
            let nameBackwards = '';
            for(let i=self.name.length-1; i>=0;i--){
                nameBackwards += self.name[i];
            }
            return nameBackwards;
        }
        return console.log(`${getReverseName()} si eman ym, ollH`);
    },
};
w.greetBackwards();

이름을 거꾸로 쓰고자 중첩된 함수 getReverseName을 사용했습니다. 하지만 의도대로 동작하지 않았습니다. q.greetBackward()를 호출하는 시점에서는 this를 의도한 대로 q에 연결하지만 greetBackwards안에서 getReverseName을 호출하면 this는 q가 아닌 다른것에 묶입니다. 이런 문제를 해결하기 위해 다른 변수에 this를 할당해서 사용하는 것

화살표 표기법

ES6에서 새로 만든 화살표 표기법도 환영받는 문법입니다. 화살표 표기법은 간단히 말해 function이라는 단어와 중괄호 숫자를 줄이려고 고안된 단축 문법입니다. 중요한 차이가 하나 있는데, 화살표 함수에는 세 가지 단축문법이 있습니다.

  • function을 생략해도 됩니다.
  • 함수에 매개변수가 단 하나 뿐이라면 괄호( )도 생략할 수 있습니다.
  • 함수 바디가 표현식 하나라면 중괄호와 return문도 생략할 수 있습니다.

화살표 함수는 항상 익명입니다. 화살표 함수도 변수에 할당할 수는 있지만 function 키워드 처럼 이름 붙은 함수를 만들 수는 없습니다. 다음 예제는 모두 같은쌍입니다.

//둘둘씩 같은 코드
const f1 = function() {return "hello";}
const f2 = () => "hello";

const r1 = function(name) { return `Hello, ${name}`; }
const r2 = name => `Hello, ${name}`;

const z1 = function(a,b) {return a + b;}
const z2 = (a,b) => a+b;

화살표 함수는 익명 함수를 만들어 다른 곳에 전달하려 할 때 가장 유용하며, 일반적인 함수와 중요한 차이점이 있습니다. this가 다른 변수와 마찬가지로, 정적으로 묶인다는 겁니다. 바로 위에서 const self = this를 하지 않고도 this.name이 참조가 가능해집니다. 또한 arguments 변수도 사용할 수 없습니다. 하지만 ES6에서 확산 연산자가 생겼으니 arguments가 필요 없긴합니다.

call

this를 지정하여 호출할 수 있다. 빠르게 코드로 확인하자

const bruce = {name:"Bruce"};
const madeline = {name:"Madeline"};

//이 함수는 어떤 객체에도 연결되지 않았지만 this를 사용합니다.
function greet(){
    return `Hello I'm ${this.name}`;
}

console.log(greet());               //Hello I'm 
console.log(greet.call(bruce));     //Hello I'm Bruce
console.log(greet.call(madeline));  //Hello I'm Madeline

함수를 호출하면서 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 객체의 메서드인 것처럼 사용할 수 있습니다. call의 첫 번째 매개변수는 this로 사용할 값이고, 매개 변수가 더 있으면 그 매개변수는 호출하는 함수로 전달됩니다.

function update(birthYear, occupation){
    this.birthYear = birthYear,
    this.occupation = occupation
}

update.call(bruce, 1994,'singer');
update.call(madeline, 1990,'actress');
console.log(bruce);
console.log(madeline);

apply

apply는 함수 매개변수 처리하는 방법을 제외하면 call과 완전히 같습니다. call은 일반적으로 함수와 마찬가지로 매개변수를 직접 받지만, apply는 매개변수를 배열로 받습니다.

update.call(bruce, [1994,'singer']);
update.call(madeline,[1990,'actress']);

apply는 배열 요소를 함수 매개변수로 사용해야 할 때 유용합니다. apply를 설명할 때 흔히 사용하는 예제는 배열의 최솟값과 최댓값을 구하는 겁니다. 자바스크립트의 내장 함수인 Math.min과 Math.max는 매개변수를 받아 그중 최솟값과 최댓값을 각각 반환합니다. apply를 사용하면 기존 배열을 이들 함수에 바로 넘길 수 있습니다.

const arr [2,3,-5,15,7];
Math.min.apply(null, arr);  //-5
Math.max.apply(null, arr);  //15

ES6의 확산 연산자(...)를 사용해도 apply와 같은 결과를 얻을 수 있습니다. update 메서드는 this값이 중요하므로 call를 사용해야 하지만 Math.min과 Math.max는 this값이 무엇이든 관계가 없으므로 확산 연산자를 그대로 사용할 수 있습니다.

const newBruce = [1940, "martial artist"];
update.call(bruce, ...newBruce);
Math.min(...arr);
Math.max(...arr);

bind

bind를 사용하면 함수의 this값을 영구히 바꿀 수 있습니다. update 메서드를 이리저리 옮기면서도 호출할 때 this 값은 항상 bruce가 되게끔, call이나 apply, 다른 bind와 함께 호출하더라도 this값이 bruce가 되도록 하려면 bind를 사용합니다.

const updateBruce = update.bind(bruce);

updateBruce(1904, 'actor');
console.log(bruce);
updateBruce.call(madeline, 1274, 'king');
console.log(bruce);

다른값은 바뀌지만 bind한 name은 바뀌지않습니다. 이건 다시 bind해도 마찬가지이고요 그래서 찾기 어려운 버그를 야기 시킬 수도 있습니다. 하지만 그렇다고 사용하지말라는건 아닙니다. 

bind는 매우 유용하지만, 함수의 this가 어디에 묶이는지 정확히 파악하고 사용해야합니다.

 

bind에 매개변수를 넘기면 항상 그 매개변수를 받으면서 호출되는 새 함수를 만드는 효과가 있습니다. 예를 들어 bruce가 태어난 해를 항상 1949로 고정하지만, 직업은 자유롭게 바꿀 수 있는 업데이트 함수를 만들고 싶다면?

const updateBruce1949 = update.bind(bruce, 1949);
updateBruce1949('singer, songwriter');
반응형
댓글
반응형
최근에 달린 댓글
글 보관함
Total
Today
Yesterday
최근에 올라온 글
«   2024/05   »
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