함수와 프로토타입 체이닝
함수정의
자바스크립트에서는 함수를 생성하는 방법은 3가지가 있다. 함수 선언문 , 함수 표현식 , Function() 생성자 함수 방법이 있다.
함수 선언문 방식
함수 선언문 방식으로 정의된 함수의 경우는 반드시 함수명이 정의되어 있어야 한다.
function add( a , b) {
return a+b;
}
함수 표현식 방식
자바스크립트에서는 함수도 하나의 값처럼 취급된다. 따라서 함수도 숫자나 문자열처럼 변수에 할당하는 것이 가능하다. 생성된 함수를 변수에 할당하여 함수를 생성하는 것을 함수 표현식이라고 한다. 함수표현식에서 함수이름은 선택이지만 , 함수이름을 사용하면 함수 내부에서 재귀함수 처리가 있다.
var factorialVar = function factorial(n) {
if ( n == 1 )
return 1;
return n * factorial(n-1) ;
}
하지만 책을보면서 이해가 안된 부분이 있는데 return n*facitoral(n-1) 대신에 n * factorialVar(n-1)을 해도 재귀호출이 되는데 굳이 저렇게 해야 하는 가 싶음.. 혹시 아시는 분이 계시다면 알려주신다면 베리베리 감사할듯합니다 ㅎ..
Function() 생성자 함수를 이용한 방식
일반적으로 자주 사용되지 않으므로 간단히 알아보도록 하겠다..
var add = new Function('x','y','return x+y') ;
함수 호이스팅
함수 선언문 형태로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작한다는 것이 함수 호이스팅이다. 이러한 함수 호이스팅은 함수를 사용하기 전에 반드시 선언해야 한다는 규칙을 무시하므로 코드의 구조를 엉성하게 만들 수도 있다고 지적하며, 함수 표현식 사용을 권장하고 있다.
add(2,3) // 5 --> 함수 호이스팅때문에 함수를 정의하기도 전에 호출했지만 에러가 발생하지 않음.
function add(a,b){
return a+b;
}
add(3,4) // 7
함수 객체
자바스크립트에서는 함수도 객체다
자바스크립트에서는 함수도 객체다. 즉 함수의 기본 기능인 코드 실행뿐만 아니라, 함수 자체가 일반 객체처럼 프로퍼티들을 가질 수 있다는 점이다.
function add(x,y){
return x+y
}
add.result = add(2,3);
add.status = "ok";
console.log(add.result) // 5
console.log(add.status) // ok
자바스크립트에서는 함수도 값으로 취급된다.
자바스크립트에서는 함수도 객체 이므로 , 이는 함수도 일반 객체처럼 취급될 수 있다는 것이다. 때문에 자바스크립트는 다음과 같은 동작이 가능하다.
1.리터럴에 의한 생성
2.변수나 배열의 요소,객체의 프로퍼티 등에 할당
3.함수의 인자로 전달가능
4.함수의 리턴값으로 리턴 가능
5.동적으로 프로퍼티를 생성 및 할당 가능
위와 같은 특징을 가지는 객체를 일급 객체라고 부르며 , 자바스크립트의 함수는 일급 객체이다.
함수 객체의 기본 프로퍼티
자바스크립트 함수 역시 객체지만 , 일반 객체와는 다르게 추가로 함수 객체만의 표준 프로퍼티가 존재한다.
ECMAScript 명세서에서는 모든 함수가 length와 prototype 프로퍼티를 가져야 한다고 기술하고 있다. 이에 대한 설명은 좀 더 뒤에서 설명하도록 하고,
함수의 name 프로퍼티는 함수의 이름 , caller 프로퍼티는 자신을 호출한 함수를 , arguments 프로퍼티는 함수를 호출할 때 전달 된 인자값을 나타낸다.
함수 객체의 부모 역할을 하는 프로토타입 객체를 Funtion.prototype 객체라고 명명하고 있으며 , 이것 역시 함수 객체이다. Function.Prototype역시 함수라면 proto 프로퍼티를 자기 자신을 참조하는 것인가? 생각할 수 있지만 ECMAScript 명세서에서는 Function.prototype의 proto는 예외적으로 Object로 한다고 기술되어 있다.
length 프로퍼티
함수 객체의 length 프로퍼티는 모든 함수가 가져야하는 프로퍼티이며 , 함수가 정상실행 될 때 기대되는 인자의 개수를 나타낸다. ( 함수에 정의한 인자갯수)
prototype 프로퍼티
모든 함수가 가져야 하는 프로퍼티이며 , 프로토타입 객체를 가리킨다. 앞 서 설명하였던 객체의 [[Prototype]]과 프로토타입을 가리킨다는 공통점은 있지만 관점의 차이가 있다.
[[Prototype]] 은 객체 입장에서 자신의 부모 역할을 하는 프로토타입객체를 가리키지만 함수의 prototype 프로퍼티는 함수 자신이 생성자함수로 역할을 할 때 생성되는 객체의 부모 역할을 하는 프로토타입 객체를 가리킨다.
prototype 프로퍼티는 함수가 생성 될 때 만들어지며, 다음 그림과 같이 단지 constructor 프로퍼티 하나만 있는 객체를 가리킨다.
함수의 다양한 형태
콜백함수
콜백함수는 코드를 통해 명시적으로 호출하는 함수가 아니라, 개발자는 단지 함수를 등록하기만 하고, 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출되는 함수를 말한다. 대표적인 예로는 자바스크립트에서의 이벤트 핸들러이다.
window.onload = function() { alert("load"} } 라는 콜백함수를 등록해 놓으면 document가 로딩완료되면 load라는 팝업이 뜬다.
즉시 실행함수
함수를 정의함과 동시에 실행되는 함수를 즉시실행함수라고 한다.
( function(num) { alert(num) ; } )(10) ;
이라는 즉시실행 함수를 정의하면 10이라는 숫자가 팝업으로 뜬다.
이렇게 함수가 선언되자마자 실행되게 만든 즉시 실행 함수의 경우, 같은 함수를 다시 호출할 수 없다. 따라서 즉시 실행 함수의 이러한 특징을 이용한다면 최초 한 번의 실행만을 필요로 하는 초기화 코드 부분 등에 사용할 수 있다. 보통 라이브러리 코드들은 즉시실행함수 형태로 감싸진 형태이다.
왜 즉시실행 함수 형태로 라이브러리를 지원할까? 그 이유는 변수 유효 범위특성 떄문인데 , 자바스크립트에서는 함수 유효 범위를 지원한다. 따라서 라이브러리 함수들을 즉시실행함수 내부에 정의를 하게 되면 전역스페이스를 더럽히지 않고 , 다른 라이브러리들과의 이름충돌을 피할 수 있기 때문이다.
내부함수
함수 내부에 정의된 함수를 내부 함수라고 부른다. 내부 함수는 자바스크립트의 기능을 보다 강력하게 해주는 클로저를 생성하거나 부모 함수 코드에서 외부에서으 ㅣ접근을 막고 독립적인 헬퍼 함수를 구현하는 용도로 사용한다.
내부함서에서는 자신을 둘러싼 부모 함수의 접근이 가능하며 , 그 이유는 자바스크립트의 스코프 체이닝 때문이다. 뒤에서 자세히 설명하도록 하겠다. 또한 내부함수는 일반적으로 자신이 정의된 부모 함수 내에서만 호출이 가능하다.
하지만 함수 외부에서도 특정 함수 스코프 안에 선언된 내부 함수를 호출할 수 있다. 가령 부모 함수 내부에서 내부 함수를 외부로 리턴하면, 부모 함수밖에서도 내부 함수를 호출하는 것이 가능하다. 이와 같이 실행이 끝난 부모 함수 스코프의 변수를 참조하는 변수를 클로저라고 한다.
함수 호출과 this
argument객체
자바스크립트에서는 함수를 호출할 때 함수 형식에 맞춰 인자를 넘기지 않더라도 에러가 발생하지 않는다. 이러한 특성 때문에 함수 코드를 작성할 때, 런타임 시에 호출된 인자의 개수를 확인하고 이에 따라 동작을 다르게 해줘야 할 경우가 있다. 이를 가능케 하는 게 바로 arguments 객체다.
arguments객체는 함수를 호출할 때 넘긴 인자들이 배열 형태로 저장 된 객체를 말한다. 이 객체는 유사배열객체이다.
함수 호출과 this 바인딩
자바스크립트에서는 함수를 호출할 때 기존 매개변수로 전달되는 인자값에 더해, 앞서 설명한 argument 객체 및 this 인자가 함수 내부로 암묵적으로 전달된다. 자바스크립트의 여러 가지 함수가 호출되는 방식에 따라 this 바인딩이 다르다.
객체의 메서드를 호출할 때 this 바인딩
메서드 내부 코드에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩 된다.
함수를 호출할 때 this 바인딩
함수를 호출할 때 해당 함수 내부 코드에서 사용된 this는 전역 객체에 바인딩된다. 브라우저에서 자바스크립트를 실행하는 경우 전역 객체는 window객체가 된다.
생성자 함수를 호출할 때 this 바인딩
기존함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다. 이는 반대로 생각하면 일반 함수에 new를 붙여 호출하면 원치 않는 생성자 함수처럼 동작할 수 있다.
동작방식
1. 빈 객체 생성 및 this 바인딩.
빈객체를 생성 한 뒤 this를 빈 객체로 바인딩한다. 엄밀히 말하자면 빈 객체는 아니며 , 생성자 함수가 생성한 빈 객체는 자신을 생성한 생성자 함수의 prototype프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
2. this를 통한 프로퍼티 생성
this를 빈 객체로 바인딩하고 , 생성 된 객체에 프로퍼티를 설정한다.
3.생성 된 객체 리턴.
리턴문이 없을 경우 , 새로 생성 된 객체가 리턴되며 , 임의의 객체를 리턴할 경우 임의의 객체라 리턴된다. 기본형을 리턴하여도 this로 설정된 빈 객체가 리턴도니다.
객체 리터럴 방식과 생성자 함수를 통한 객체생성 방식의 차이
1. 객체 리터럴 방식으로 생성된 객체는 같은 형태의 객체를 재 생성 할 수 없지만 , 생성자 함수로 생성 된 객체는 같은 형태의 객체를 재생성할 수 있다.
2. 프로토 타입 객체가 다르다. 객체리터럴 방식으로 생성 된 객체의 프로토타입은 Object.prototype을 가리키며 , 생성자 함수로 생성 된 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
call 과 apply 메서드를 이용한 명시적인 this 바인딩.
자바스크립트는 이러한 내부적인 this 바인딩 이외에도 this를 특정 객체에 apply , call 메서드를 이용하여 명시적으로 바인딩 시키는 방법이 있다..
call과 appley는 기능자체는 동일하며 , 호출하는 방법에 차이가 있다.
apply 메서드는 첫 번째 인자에 바인딩 시킬 객체를 전달하고 , 두 번째 인자에는 함수에 전달할 매개변수를 배열 형태로 전달한다. call은 apply와 달리 매개변수를 배열형태가 아닌 개별로 전달한다.
함수리턴
1. 일반 함수나 메서드는 리턴값을 지정하지 않을 경우, undefined 값이 리턴된다.
2.생성자 함수에서 리턴값을 지정하지 않을 경우 생성 된 객체가 리턴된다.
프로토타입 체이닝
자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 프로토타입 객체를 자신의 부모 객체로 설정하는 [Prototype] 링크로 연결한다.
객체 리터럴 방식으로 생성된 객체의 프로토타입 체이닝
객체 리터럴로 생성한 객체는 Object() 라는 내장 생성자 함수로 생성된 것이다. Object() 라는 생성자 함수도 함수 객체이므로 prototype이라는 프로퍼티 속성이 있다. 따라서 앞선 규칙에 따라 객체 리터럴로 방식으로 생성된 객체는 Object.prototype을 프로토타입 객체로 연결한다.
프로토타입 체이닝 : 특정 객체의 프로퍼티나 메서드에 접근하려고 할 때 , 해당 객체에 접근하려는 프로퍼티 또는 메서드가 없다면 [Prototype] 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티를 차례대로 검색하는 것.
생성자 함수로 생성된 객체의 프로토타입 체이닝
자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 취급한다.