1. 함수란
2. 함수 선언문으로 함수 정의
자바스크립트는 기본적으로 function 키워드와 그 옆에 인수를 담는 괄호 기호를 통해 정의한다. 이때 함수 정의문의 인수를 인자(parameter)라고 부른다. 인수는 복수가 될 수 있다. 또한 반환값으로 return문을 사용한다.
function f() {
return;
}
이밖에도 함수를 정의하는 방법은 다양하다. ES6 이후에는 화살표 함수 표현식을 통해 function 키워드를 생략하는 방법도 추가되었다.
3. 함수의 실행 흐름
4. 함수 선언문의 끌어올림(Hoisting)
자바스크립트 엔진은 변수 선언문과 마찬가지로 함수 선언문을 프로그램의 첫머리로 끌어올린다. 이것을 호이스팅된다라고 표현하는데 예시는 다음과 같다.
<script>
document.addEventListener("DOMContentLoaded", function () {
console.log(hoist(10));
function hoist(arg) { return arg * arg; }
});
</script>
변수 선언문도 마찬가지로 호이스팅 되는데 이 부분은 선언자인 var, {let, const}에 따라 실제로 동작 방식에 있어서 차이가 있다. 이 부분은 변수의 hoisting에서 알아보도록 하자.
5. 값으로서의 함수
함수 정의에서도 말했듯이 자바스크립트에서 함수는 객체이다. 함수 선언문으로 함수를 선언했을 때 내부적인 동작에 의해 함수 이름을 변수 이름으로 한 변수와 함수 객체가 만들어진다. 그리고 그 변수에 함수 객체의 참조가 저장된다.
따라서 이 변수를 다른 변수에 할당할 수 있다. 또한 다른 함수의 인수로 사용도 가능하다.
<script>
document.addEventListener("DOMContentLoaded", function () {
// 다른 변수에 할당
console.log(hoist(10));
function hoist(arg) { return arg * arg; }
let assign = hoist;
console.log('assign value:',assign(100)); // assign value: 10000
// 다른 함수의 인수로 사용
function assignHoist(arg) {
return arg;
}
let assignFunction = assignHoist(hoist(20));
console.log('assignFunction value:', assignFunction); // assignFunction value: 400
});
</script>
내부적으로 hoist는 아래와 같은 형태이다.
[변수 hoist가 함수 객체를 참조하고 있음]
hoist | function(arg) {return arg * arg; } |
6. 값에 의한 호출과 참조에 의한 호출
함수는 원시 값을 인수로 넘겼을 때와 객체를 인수로 넘겼을 때 다르게 동작한다.
<script>
document.addEventListener("DOMContentLoaded", function () {
function f1(a) {
return a = a + 1;
}
let x = 3;
let y = f1(x);
console.log('x :', x, 'y :', y); // x : 3, y : 4
});
</script>
값의 전달 : 변수 a와 x는 다른 영역의 메모리에 위치한 별개 변수. a값이 변경되더라도 x값은 바뀌지 않음
<script>
document.addEventListener("DOMContentLoaded", function () {
function f2(arg) {
arg.a = arg.a + 1;
return arg;
}
let xx = { a:10 };
let yy = f2(xx);
console.log('xx:'xx, 'yy:'yy); // xx : Object {a=11}, yy : Object {a=11}
});
</script>
※ 함수에서 복수의 인수를 넘기게 될 경우 인수 개수가 추가되거나 변경되면 함수 자체를 바꿔야 하므로 유지 보수에 힘들다. 이 경우 인수를 객체의 property로 담아서 객체를 생성하고 그 객체를 인자로 넘긴다면 훨씬 더 코드가 정돈될 수 있다.
[변경 전]
인수가 추가되거나 순서가 변경되면 인자 받는 부분 및 내부 로직, 즉 함수 전체를 다시 정의해야 한다.
document.addEventListener("DOMContentLoaded", function () {
// 복수의 인수일 때 깔끔하게 인자로 넘기는 방법
// 변경 전
let a = 1;
let b = 2;
let c = 3;
function beforeSetProperties(a, b, c) {
return a + b + c;
}
beforeSetProperties(a, b, c);
});
[변경 후]
인수가 추가되면 함수의 인자로 받는 객체를 찾아서 property만 추가하고 내부 로직만 수정하면 된다.
이렇게 할 경우 함수에 필요한 인수를 객체 형태 묶어서 관리할 수 있다.
document.addEventListener("DOMContentLoaded", function () {
// 복수의 인수일 때 깔끔하게 인자로 넘기는 방법
// 변경 후
let parameters = {
a : 1,
b : 2,
c : 3
}
function setProperties(params) {
return params.a + params.b + params.c;
}
setProperties(parameters);
});
7. 변수의 유효 범위(scope)
유효 범위: 변수에 접근할 수 있는 범위
[유효 범위를 결정하는 방법]
자바스크립트는 어휘적 범위를 채택하고 변수의 유효 범위에 따라 전역 변수와 지역 변수로 나눠진다.
자세한 건 코드를 통해 알아보자
let parameters = { // 전역변수
a : 1,
b : 2,
c : 3
}
function setProperties(params) {
let abc = 10; // 지역변수
console.log('abc:', abc); // abc : 10
console.log('params:', params) // params : {a:1, b:2, c:3}
console.log('parameters:', parameters); // parameters : {a:1, b:2, c:3}
return params.a + params.b + params.c;
}
console.log('parameters:', parameters); // parameters : {a:1, b:2, c:3}
console.log('abc:', abc); // Uncaught ReferenceError: abc is not defined
console.log('params:', params); // Uncaught ReferenceError: params is not defined
console.log('setProperties:', setProperties(parameters)); // setProperties: 6
함수를 기준으로 볼 때 parameters 변수는 함수 밖에 선언되어 전역 변수로 프로그램 전체를 유효 범위로 갖기 때문에
setProperties 함수 안, 밖 모두 참조가 가능하다.
반면에 setProperties 함수 안에 선언된 abc 변수는 해당 함수 블록 안에서만 유효한 지역변수이다.
따라서 함수 안에선 abc에 대해 참조가 가능하지만 함수 밖에서 참조하려고 하면 해당 변수가 remove 되었기 때문에
'abc is not defined'라는 참조 오류가 발생한다.
마찬가지로 setProperties의 인자로 쓰인 params도 함수 내부에서 참조가 가능하지만 밖에서 참조할 때 참조 오류가 발생하므로 지역변수인 것을 알 수 있다.
8. 함수 리터럴로 함수 정의
함수 선언문으로 정의한 것을 함수 리터럴로 바꾸면 다음과 같다.
// 함수 선언문
function ff(x) { return x; }
// 함수 리터럴
let ff = function(x) { return x; };
함수 리터럴은 이름이 없기 때문에 익명 함수라고 칭한다. 또한 두 방식의 차이점은 함수 선언문 같은 경우 블록을 닫을 때 세미콜론이 필요 없지만 리터럴로 선언할 경우 반드시 세미 콜론이 필요하다.
사용법 또한 ff(x)라고 사용하면 함수가 실행
두 방식의 차이점은 함수 선언문 같은 경우 프로그램 첫머리로 Hoisting이 된다고 했지만 함수 리터럴로 선언할 경우 Hoisting이 되지 않는다. 즉, 변수에 할당한 후에 비로소 ff라는 이름을 갖게 되고 호출할 수 있다는 것이다.
// 함수 리터럴
console.log(ff(3)); //Uncaught TypeError: ff is not a function
var ff = function(x) {
return x;
}
이러한 익명 함수에는 이름을 붙일 수 있는데 위의 코드를 아래와 같이 이름을 붙여보았다.
var ff = function fff(x) {
return x;
}
이렇게 익명 함수에 이름을 붙이게 되면 코드에서 fff라는 이름은 함수 안에서만 유효하고 함수 밖에선 fff라는 이름으로 함수를 호출할 수가 없다. 이럴 경우 디버거를 할 때 이름이 붙은 익명 함수이기 때문에 구별할 수 있다는 장점이 있다.
var ff = function fff(x) {
console.log(fff.arguments); // Arguments [3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
return x;
}
console.log(fff); // Uncaught ReferenceError: fff is not defined
ff(3);
9. 객체의 메서드
앞서 변수를 객체 형태로 만드는 것도 알아봤는데 이 객체의 property 값에 함수 객체의 참조를 대입할 수 있다. 이럴 경우 해당 property를 메서드라고 부른다. 메서드를 정의할 때 해당 함수 객체는 함수 리터럴, 즉 익명 함수여야 한다. 또한 객체 생성 이후 나중에 추가할 수 있다.
// 객체의 매서드
let customProperties = {
a : -10,
b : 2.6,
c : 3.4,
calc : function () {
return (Math.abs(this.a) - Math.round(this.b)) + Math.ceil(this.c);
}
}
console.log('customProperties.a:', customProperties.a); //customProperties.a: -10
console.log('customProperties.b:', customProperties.b); //customProperties.b: 2.6
console.log('customProperties.c:', customProperties.c); //customProperties.c: 3.4
console.log('customProperties.calc:', customProperties.calc()); //customProperties.calc: 11
함수 객체 안에 적힌 this는 디스 바인딩 컴포넌트에 의해 이 함수를 메서드로 가지고 있는 객체인 customProperties를 가리킨다. 즉 this.a == customProperties.a이다. 메서드를 호출할 때는 일반 함수 실행과 동일하게 소괄호를 붙여야 한다.
일반적으로 메서드는 해당 메서드가 속한 객체의 다른 property의 데이터와 상태를 변경할 때 사용한다.
// 객체를 참조값으로 갖는 d라는 이름의 property와 change라는 이름의 메서드 추가
let customProperties = {
a : -10,
b : 2.6,
c : 3.4,
d : { x : 100, y : 200 },
calc : function () {
return (Math.abs(this.a) - Math.round(this.b)) + Math.ceil(this.c);
},
change : function (i, j) {
this.d.x = this.d.x + i;
this.d.y = this.d.y + j;
}
}
customProperties.change(10, 20);
console.log('customProperties.d:', customProperties.d); // customProperties.d: {x: 110, y: 220}
아래처럼 change 메서드를 통해 객체 내부의 다른 property d의 데이터를 변경시켰다.
데이터와 데이터의 상태를 변경할 수 있는 메서드를 객체 안에 담아서 이러한 객체들이 모이고 사용해 프로그램을 제작하는 방식을 객체 지향 프로그래밍이라고 한다.
10. 함수를 활용하면 얻는 장점
11. 함수는 객체
자바스크립트 함수는 Function 객체이다. 또한, 아래와 같은 특징이 있는 객체를 일급 객체라고 부르고. 일급 객체인 함수는 일급 함수라고 부른다. C, Java의 프로그래밍 언어의 함수는 일급 함수가 아니다. 때문에 스킴 등의 함수형 언어처럼 함수형 프로그래밍이 가능하다.
[JavaScript] 자바스크립트의 this (0) | 2020.08.18 |
---|---|
[JavaScript] 자바스크립트 배열의 기초 (0) | 2020.08.18 |
[JavaScript] 자바스크립트 객체의 기초 (0) | 2020.08.17 |
[JavaScript] 자바스크립트 데이터 타입 (0) | 2020.08.17 |
[JavaScript] 자바스크립트 함수의 정의 (0) | 2020.08.13 |