함수[Learning JavaScript]

함수

// sayHello라는 함수를 선언
function sayHello() {
	//함수 바디는 여는 중괄호로 시작하고
    console.log("hello World");
    //닫는 중괄호로 끝난다.
}

// 함수를 호출
sayHello(); //hello World

반환값

  • return 키워드를 사용하면 함수를 종료하고 값을 반환한다.
  • return을 명시적으로 호출하지 않으면 반환 값은 undefined가 된다.
function sayHello() {
	return "hello World";
}

sayHello(); //hello World

호출과 참조

  • 함수 식별자 뒤에 괄호를 쓰면 함수를 호출하고 함수의 표현식은 반환 값이 된다.
  • 괄호를 쓰지 않으면 다른 값과 마찬가지로 함수를 참조하고 함수를 실행하지 않는다.
sayHello(); //hello World
sayHell; //function sayHello()
  • 함수를 변수에 할당하면 다른 이름으로 함수를 호출할 수 있다.
const f = sayHello;
f(); //hello World
  • 함수를 객체 프로퍼티에 할당할 수도 있다.
const o = {};
o.f = sayHello;
o.f(); //hello World
  • 배열요소로 할당할 수 있다.
const arr = [1,2,3];
arr[1] = sayHello; //arr은 [1, function sayHello(), 2]
arr[1](); //hello World

함수와 매개변수

//a와 b를 정해진 매개변수라고 한다.
//함수가 호출되면 정해진 매개변수는 값을 받아 실제 매개변수가 된다.
function avg(a, b) {
	return (a+b)/2;
}

function f(o) {
	o.message = "f에서 수정함";
    o = {
    	message : "새로운 객체"
    };
    console.log(`f 내부 : ${o.message}`)
}

let o ={
	message : '초기값'
};

console.log(`f 호출 전 : ${o.message}`);

f(o);
console.log(`f 호출 후 : ${o.message}`);

==출력
f 호출  : 초기값
f 내부 : 새로운 객체
f 호출  : f에서 수정함

//함수 내부의 매개변수 o와 함수 바깥의 변수 o가 다르다.
//f를 호출하면 둘은 같은 객체를 가리키지만, f내부에서 o에 할당한 객체는 새로운 객체이다.

매개변수 해체

  • 프로퍼티 이름은 반드시 유효한 식별자여야 한다.
  • 들어오는 객체에 해당 프로퍼티가 없는 변수는 undefined를 할당.
function getSentence({subject, verb, object}) {
	return `${subject} ${verb} ${object}`;
}

const o = {
	subject : "I",
    verb : "love",
    object : "Javascript",
};

getSentence(o); //I love Javascript
  • 배열도 해체할 수 있다.
function getSentence([subject, verb, object]) {
	return `${subject} ${verb} ${object}`;
}

const o = ["I", "love", "Javascript"];
getSentence(o); //I love Javascript
  • 확산 연산자(…) 써서 남는 매개변수를 이용할 수 있다.
  • 함수를 선언할 때 확산 연산자는 반드시 마지막 매개변수여야 한다.
function addPrefix(prefix, ...words) {
	const prefixedWords = [];
    for(let i=0; i<words.length; i++) {
    	prefixedWords[i = prefix + words[i]
    }
	return prefixedWords;
}

addprefix("con", "verse", "vex"); // ["convers", "convex"];

매개변수 기본값

  • ES6에서는 매개변수에 기본값을 지정하는 기능이 추가되었다.
function f(a, b="default", c=3) {
	ruetnr `${a} - ${b} - ${c}`;
}

f(5, 6, 7); //"5 - 6 - 7"
f(5, 6); //"5 - 6 - 3"
f(5); //"5 - default - 3"
f(); //"undefiend - default - 3"

객체의 프로퍼티인 함수

  • 객체의 프로퍼티인 함수를 메소드라고 불러서 일반적인 함수와 구별한다.
  • 객체 리터럴에서도 메서드를 추가할 수 있다.
cost o = {
	name : "Wallace", //원시 값 프로퍼티
    bark : function() { return 'Woof'; } //함수 프로퍼티(메소드)
}

//ES6에서 간편하게 추가할 수 있는 문법
cost o = {
	name : "Wallace", //원시 값 프로퍼티
    bark() { return 'Woof'; } //함수 프로퍼티(메소드)
}

this키워드

  • 일반적으로 this는 객체의 프로퍼티인 함수에서 의미가 있다.
  • 메서드를 호출하면 this는 호출한 메서드를 소유하는 객체가 된다.
function o = {
	name : 'Wallace',
    speak() { return 'My name is ${this.name}!'},
}

o.speak(); //My name is Wallace!
  • this는 함수를 어떻게 선언했느냐가 아니라 어떻게 호출했느냐에 따라 달라진다.
function o = {
	name : 'Wallace',
    speak() { return 'My name is ${this.name}!'},
}

cost speak = o.speak;
speak === o.speak; //true; 두 변수는 같은 함수를 가리킨다.
speak(); //My name is undefined!
  • 중첩된 함수안에서 this를 사용할 때 문제점 및 해결방법.
cost o = {
	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 `${getReverseName()} si eman ym ,olleH`;
    }
};
//o.greetBackwards()를 호출하는 시점에서는 this가 o에 연결
//greetBackwards안에서 getReversename을 호출하면 this는 o가 아닌 다른 것에 묶임.
o.greetBackwards();


//해결방법은 다른변수에 this를 할당한다.
cost o = {
	name : 'Julie',
    greetBackwards : function() {
    	cost self = this;
    	function getReverseName() {
        	let nameBackwards = '';
            for(let i=self.name.length-1; i>=0; i--) {
            	nameBackwards += self.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym ,olleH`;
    }
};
o.greetBackwards();

함수 표현식과 익명 함수

  • 익명함수에서는 함수에 식별자가 주어지지않는다.
  • 함수 표현식은 함수 이름을 생략할 수 있다는 점을 제외하면 함수 선언과 같다.
const f = function() {
	// ...
}
  • 함수 표현식에서 함수에 이름을 정하고 다시 변수에 할당할 경우.
//이름 g에 우선순위가 있다.
//함수 바깥에서 함수에 접근할 때는 g를 써야하며, f로 접근불가.
const g = function f() {
	// ...
}
  • 함수 안에서 자신을 호출할 때(재귀) 이런 방식이 필요하다.
//함수안에서는 f를 써서 자기 자신을 참조
//함수 바깥에서는 g를 써서 함수를 호출
const g = function f(stop) {
	if(stop) console.log('f stop');
    f(true);
};
g(false);
  • 나중에 호출할 생각으로 함수를 만든다면 함수 선언을 사용.
  • 다른 곳에 할당하거나 다른 함수에 넘길 목적으로 함수를 만든다면 함수 표현식을 사용.

화살표 표기법

  • function이라는 단어와 중괄호 숫자를 줄이려고 고안된 단축 문법이다.
    • function을 생략해도 된다.
    • 함수에 매개변수가 단 하나 뿐이라면 괄호(())도 생략할 수 있다.
    • 함수 바디가 표현식 하나라면 중괄호와 return 문도 생략할 수 있다.
  • 화살표 함수는 항상 익명이다. 익명함수를 만들어 다른 곳에 전달하려 할 때 유용하다.
const f1 = function() {return "hello!"; }
//또는
const f1 = () => "hello!";

const f2 = function(name) { return `Hello, ${name}!`; }
//또는
const f2 = name => `Hello, ${name}`;

const f3 = function(a,b) { return a+b; }
//또는
const f3 = (a,b) => a + b;
  • this가 다른 변수와 마찬가지로, 정적으로 묶인다.
  • 화살표 함수를 사용하면 내부 함수 안에서 this를 사용할 수 있다.
cost o = {
	name : 'Julie',
    greetBackwards : function() {
    	cost getReverseName = () => {
        	let nameBackwards = '';
            for(let i=this.name.length-1; i>=0; i--) {
            	nameBackwards += this.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym ,olleH`;
    }
};
o.greetBackwards();

call과 apply, bind

call

  • call 메서드는 모든 함수에서 사용할 수 있으며, this를 특정 값으로 지정할 수 있다.
const bruce = {name:"Bruce"};
const madeline = {name:"Madeline"};

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

greet(); 				//"Hello, I'm undefined!" - this는 어디에도 묶이지 않음.
greet.call(bruce);		//"Hello, I'm Bruce!" - this는 Bruce.
greet.call(madeline);	//"Hello, I'm Madeline!" - this는 madeline.
  • 함수를 호출하면서 call을 사용하고, this로 사용할 객체를 넘기면 해당 함수가 주어진 객체 메서드인 것처럼 사용할 수 있다.
  • call의 첫 번째 매개변수는 this로 사용할 값이고, 매개변수가 더 있으면 그 매개변수는 호출하는 함수로 전달된다.
function update(birthYear, occupation) {
	this.birthYear = birthYear;
    this.occupation = occupation;
}

update.call(bruce, 1949, 'singer');
//bruce는 이제 {name:bruce, birthYear:1949, occupation:singer};

apply

  • apply는 함수 매개변수를 처리하는 방법을 제외하면 call과 같다.
  • apply는 매개변수를 배열로 받는다.
function update(birthYear, occupation) {
	this.birthYear = birthYear;
    this.occupation = occupation;
}

update.call(bruce, [1949, 'singer']);
//bruce는 이제 {name:bruce, birthYear:1949, occupation:singer};
  • apply는 배열 요소를 함수 매개변수로 사용해야 할 때 유용한다.
const arr = [2, 3, -5, 15, 7];
Math.min.apply(null, arr); //최솟값 -5
Math.max.apply(null, arr); //최댓값 15

binde

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

const updateBruce = update.binde(bruce);

updateBruce(1904, "actor");
//bruce는 이제 {name:bruce, birthYear:1904, occupation:actor};

updateBruce(maeline, 1274, "king");
//bruce는 이제 {name:bruce, birthYear:1274, occupation:king};
// madeline은 변하지 않는다.
  • bind는 매우 유용하지만, 함수의 this가 어디에 묶이는지 정확히 파악하고 사용해야한다.
  • bind에 매개변수를 넘기면 항상 그 매개변수를 받으면서 호출되는 새 함수를 만드는 효과가 있다.
//bruce가 태어난 해를 항상 1949로 고정하지만, 직업은 바꿀수 있는 업데이트 함수 구현
const updateBruce1949 = update.bind(bruce, 1949);
updateBruce1949("singer, songwriter");
//bruce는 이제 {name:bruce, birthYear:1949, occupation:singer, songwriter};