함수는 생각보다 복잡하다.
자바스크립트에서 함수는 일급객체
이므로 아래와 같은 특징이 있다
일급
: 사용할 때 다른 요소들과 아무런 차별이 없다는 것을 뜻한다.
// 기명 함수 표현식(named function expression)
var foo = function multiply(a, b) {
return a * b;
};
// 익명 함수 표현식(anonymous function expression)
var bar = function(a, b) {
return a * b;
};
console.log(foo(10, 5)); // 50
console.log(multiply(10, 5)); // Uncaught ReferenceError: multiply is not defined
// 함수 표현식에서 사용한 함수명은 외부 코드에서 접근 불가능하기 때문에 에러 발생
함수는 일급객체이기 때문에 변수에 할당
할 수 있는데 이 변수는 함수명이 아니라 할당된 함수를 가리키는 참조값을 저장
하게 된다. 함수 호출시 함수명이 아니라 함수를 가리키는 변수명
을 사용하여야 한다.
function square (number) { // 함수 선언문
return number * number;
}
/* 함수 선언문으로 정의한 함수의 경우, 함수명으로 호출이 가능했던 이유는
자바스크립트 엔진에 의해 아래와 같은 함수 표현식으로 형태가 변경되었기 때문이다.
*/
var square = function square(number) {
return number * number;
}
함수명과 함수 참조값을 가진 변수명이 일치하므로 함수명으로 호출되는 듯 보이지만 사실은 변수명으로 호출된 것이다.
결국 함수 선언문도 함수 표현식과 동일하게 함수 리터럴 방식으로 정의되는 것이다.
이것은 결국 내장 함수 Function 생성자 함수로 함수를 생성하는 것을 단순화시킨 short-hand(축약법)이다.
즉, 함수를 정의하는 방식은 달라도 결국 Function 생성자 함수로 함수를 생성하는 것이다.
함수의 호이스팅은 두가지 방식으로 나뉜다.
함수 선언문으로 정의된 함수는 자바스크립트 엔진이 스크립트가 로딩되는 시점에 바로 초기화하고 이를 VO(variable object)에 저장
한다. 즉, 함수 선언, 초기화, 할당이 한번에 이루어진다. 그렇기 때문에 함수 선언의 위치와는 상관없이 소스 내 어느 곳에서든지 호출
이 가능하다
var res = square(5); // undefined
function square(number) {
return number * number;
}
함수 표현식의 경우 함수 호이스팅이 아니라 변수 호이스팅
이 발생한다.
변수 호이스팅은 변수 생성 및 초기화와 할당이 분리되어 진행된다. 호이스팅된 변수는
undefined
로 초기화 되고실제값의 할당은 할당문
에서 이루어진다.
var res = square(5); // TypeError: square is not a function
var square = function(number) {
return number * number;
}
객체형(참조형) 인수는 Call-by-reference(참조에 의한 호출)
로 동작한다.
함수 호출 시 참조 타입 인수를 함수에 매개변수로 전달할 객체의 참조값
이 매개변수에 저장되어 함수로 전달된다. 따라서 함수 내에서 참조값을 이용해 값을 변경하면 참조형의 인수값도 변경된다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
obj.gender = 'female';
}
var num = 100;
var obj = {
name: 'Lee',
gender: 'male'
};
console.log(num); // 100
console.log(obj); // Object {name: 'Lee', gender: 'male'}
changeVal(num, obj);
console.log(num); // 100
console.log(obj); // Object {name: 'Kim', gender: 'female'}
함수는 객체이므로 프로퍼티를 가질 수 있다.
함수가 갖는 프로퍼티로는 arguments
, caller
, length
, name
가 있다.
arguments 객체는 함수 호출 시 전달된 인수(argument)들의 정보
를 담고 있는 순회가능한(iterable) 유사 배열 객체(array-like object)
이며 함수 내부에서 지역변수처럼 사용
된다.
유사배열객체
란 length 프로퍼티를 가진 객체를 말한다.
배열이 아니므로 배열 메소드를 사용하는 경우 에러가 발생하게 된다.
function multiply(x, y) {
console.log(arguments);
return x * y;
}
multiply(); // {}
multiply(1); // { '0': 1 }
multiply(1, 2); // { '0': 1, '1': 2 }
multiply(1, 2, 3); // { '0': 1, '1': 2, '2': 3 }
caller는 자신을 호출한 함수
를 의미한다.
function foo(func) {
var res = func();
return res;
}
function bar() {
return 'caller : ' + bar.caller;
}
console.log(foo(bar)); // caller : function foo(func) {...}
console.log(bar()); // null (browser에서의 실행 결과)
length 프로퍼티는 함수 정의 시 작성된 매개변수 갯수
를 의미한다.
function bar(x) {
console.log(arguments.length)
return x;
}
console.log(bar.length) // 1
console.log(bar(3,4,5)) // arguments.length: 3
프로토타입 객체
란 프로토타입 기반 객체 지향 프로그래밍의 근간을 이루는 객체로서 객체간의 상속(Inheritance)
을 구현하기 위해 사용된다
즉, 다른 객체에 공유 프로퍼티를 제공
하는 객체를 말한다.
prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수
로 사용될 때, 생성자 함수가 생성한 인스턴스의 프로토타입 객체를 가리킨다.
proto 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype
객체의 프로퍼티이다. 모든 객체는 상속을 통해 __proto__ 접근자 프로퍼티는 사용
할 수 있다.
// 객체는 __proto__ 프로퍼티를 소유하지 않는다.
console.log(Object.getOwnPropertyDescriptor({}, '__proto__'));
// undefined
// __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티이다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));
// {get: ƒ, set: ƒ, enumerable: false, configurable: true}
// 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
console.log({}.__proto__ === Object.prototype); // true
즉시 실행 함수(IIFE, Immediately Invoke Function Expression)란 함수의 정의와 동시에 실행되는 함수
를 말한다. 최초 한번만 호출되며 다시 호출할 수는 없다.
// 기명 즉시 실행 함수(named immediately-invoked function expression)
(function myFunction() {
var a = 3;
var b = 5;
return a * b;
}());
// 익명 즉시 실행 함수(immediately-invoked function expression)
(function () {
var a = 3;
var b = 5;
return a * b;
}());
당장 사용하는 데 있어서 문제 되는 게 없어 깊이 알 필요 없다고 생각했었다. 하지만 그건 내가 너무 거만해져 있던 건 아닌가 싶다.
참고
자바스크립트의 기본