JavaScript에서 함수는 객체이다.
객체가 prototype
객체라는 숨겨진 링크를 갖고 있듯이
함수 또한 Object.prototype
과 연결된 Function.prototype
객체를 갖고 있다.
또한 함수는 기본적으로 prototype
property를 갖고 만들어지는데,
이 property 값은 함수를 값으로 하는 constructor
property이다.이에 대한 설명 뒤에 더 자세하게 나옴
함수는 변수든, 객체든, 배열이든 자유롭게 저장될 수 있으며
심지어 인수로 전달되거나 return 값으로도 반환될 수 있다.
또한, 함수 자체가 객체이기에 함수 또한 메소드를 가질 수 있다.
var add = function (a,b) {
return a + b;
함수 리터럴은 네가지 파트로 나누어져 있다.
function 키워드
함수 이름 (Optional)
위의 예 처럼 이름 안 적으면, 익명(anonymous) 함수라고 한다.
함수 선언문 vs 함수 표현식 차이점
- 문법
// 함수 선언문 function sum(a, b) { return a + b; } // 함수 표현식 let sum = function(a, b) { return a + b; };
- 함수 생성 시점
함수 표현식은 실제 실행 흐름이 해당 함수에 도달해야 함수를 생성한다.
반면, 함수 선언문은 함수가 정의 되기 전에도 호출할 수 있다.
그 이유는, 자바스크립트 내부에서 스크립트를 실행하기 전,
전역에 선언된 함수 선언문을 찾아 해당 함수를 생성하기 때문이다.- 스코프
함수 선언문은 선언된 코드 블럭 내에서만 사용 가능함
만약 코드 블럭 밖에서 쓰고 싶다면,
밖에서 선언한 변수에, 함수 표현식으로 만든 함수를 할당해야 함함수 선언문을 먼저 고려하되,
조건에 따라 함수를 선언해야 되는 경우와 같을 때는 함수 표현식 사용해야 함
함수의 인자
중괄호로 묶여지는 명령들
이런 함수 리터럴은 어디서든 사용 가능하다.
물론 함수 안에 함수를 선언하는 것도 가능하고,
외부 컨텍스트를 이용하는 함수 객체를 closure
라고 하는데 후에 설명..
읽어볼만한 글
모든 함수는 실행 시, this 객체와 인수라는 2가지 종류의 변수를 전달 받는다.
this 객체는 객체 지향 프로그래밍 측면에서 굉장히 중요한 개념인데,
그 value는 다음과 같은 4가지 호출 방식에 의해 결정된다.
호출 시, 함수 인자만큼의 변수를 전달하지 않더라도 런타임 오류가 나지 않고,
전달되는 값이 무시되거나 undefined가 할당된다.
또한, 인자에 type check 따윈 하지 않는다.
모든 인자에는, 모든 type의 변수가 전달될 수 있다. (typescript의 등장 이유)
만약 함수가 객체의 property로 저장되면, Method라고 한다.
Method가 호출되면, this
는 객체에 묶이게 된다.
따라서 Method가 객체의 context를 이용할 수 있게 됨
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment(); //Method Invocation
document.writeln(myObject.value); //value == 1 값 변경됨
var sum = add(3, 4);
객체의 프로퍼티가 아닌 함수일 경우, 호출 방식을 function invocation이라 한다.
이 경우, this는 글로벌 객체에 바인딩되게 된다.
저자는 이를 디자인적 문제라고 지적하는데,
예를 들어, 메소드 안에 inner 함수가 있을 경우,
원래대로라면 메소드의 this에 묶이는 것이 자연스러운데
global 객체에 묶이면서 object에 대한 접근을 할 수 없게 된다는 것
다행히 이를 해결하는 방법이 있다.
myObject.double = function () {
var that = this; // helper 안에서 this를 가르켜줄 that을 만듬
var helper = function () {
that.value = add(that.value, that.value);
};
helper();
};
myObject.double();
뿐만 아니라, 어찌보면 당연하게도,
객체 메서드가 객체 내부가 아닌 다른 곳에 전달되어 호출되면 this가 사라진다.
let user = {
firstName: "John",
sayHi() {
alert(`Hello, ${this.firstName}!`);
}
};
setTimeout(user.sayHi, 1000); // Hello, undefined!
이 경우, user.sayHi 함수 모양만 setTimeout에 전달해서 실행 중이기에
method invocation이 아닌, function invocation이기에
this
에는 window
객체가 할당되고,
window
객체에는 firstName
이 없기에 undefined
가 출력된다.
만약 함수가 new 키워드와 함께 호출되면 constructor invocation이라 함
// 생성자 함수 정의
var Quo = function (string) {
this.status = string;
};
// 생성자 함수 조작
Quo.prototype.get_status = function () {
return this.status;
};
var myQuo = new Quo("confused"); //생성자 함수 호출
위의 예제에서처럼 생성자를 담는 변수는 대문자로 시작하는 것이 관습이다.
만약 생성자를 new 키워드 없이 부르면 참사가 일어나기에
다른 변수 이름과 구분되게 대문자로 지정해두고 조심하는 것이 좋다.
다만, 생성자 함수는 추천되지 않는다. 더 좋은 방법이 있음
함수를 apply
(call
,this
)를 이용해 호출하는 방법이 있다.
apply
메소드는 2가지 인자를 받는다.
첫번째 인자는 this를 바인딩할 객체를 받고,
두번째 인자는 호출하는 함수의 인자를 배열의 형태로 받는다.
함수 호출 시 사용가능한 보너스 인자가 있는데, 바로 arguments
배열이다.
arguments
는 유사 배열 객체로, 길이 정보는 있지만 배열 메서드는 없다.
이를 이용해 인자 갯수를 정해두지 않고,
받은 인자를 전부 더하는 sum 함수를 만들 수도 있다.
var sum = function () {
var i, sum = 0;
for (i=0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
};
console.log(sum(4,8,15,16,23,42)) // 108
자바스크립트는 throw
라는 예외 처리 매커니즘을 제공한다.
이 throw
객체에는 보통 2가지 property가 들어있다.
name
property - 예외의 타입message
property - 예외 설명ex)stack
)var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
}
return a + b;
}
그 후 try
명령을 통해 예외 객체를 catch
한다.
var try_it = function () {
try {
add("seven");
} catch (e) {
console.log(e.name + ': ' + e.message);
}
}
try_it( ); // TypeError: add needs numbers
try
명령문은 하나의 catch
블록을 가질 수 있기에,
여러 가지 에러를 처리하고 싶다면, 에러의 타입을 이용해 다루어야 할 것이다.
스코프는 변수의 가시성과 생명주기를 관리한다.
변수 이름 충돌과 자동 메모리 관리를 담당하기에 굉장히 중요한 기능임
사실 많은 언어들은 블록 스코프를 갖고 있기에,
한 코드 블록이 끝나면, 그 안에서 선언된 변수들은 블록이 끝나면 제거된다.
자바스크립트에서 var
로 선언된 객체는 함수 스코프를 이용하고
let
, const
로 선언된 객체는 블록 스코프를 이용한다.
스코프에 대한 좋은 소식은, inner 함수가 외부 변수에 접근 가능하다는 것이다.
외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 closure라 하는데
몇몇 언어에서는 이를 구현하기가 어렵거나, 불가능하기도 하다.
하지만 자바스크립트에서는 모든 함수가 자연스럽게 클로저가 됨
var myObject = function () {
var value = 0;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function () {
return value;
}
};
}( );
심지어 위의 예제를 보면
함수가 myObject
에 value
를 이용하는 메소드를 return하고 끝난다.
그럼 함수가 끝났으니 value
도 사라지는게 아닌가 생각할 수 있지만,
myObject
에 value
를 이용하는 메소드가 살아있으므로
프로그램이 끝날 때까지, 혹은 메소드가 살아있을 때까지 value
는 유지된다.
중요한 것은 value의 값을 받는게 아니라, value의 값에 접근이 가능하다는 개념이다.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
};
}
};
위의 예제를 보면, alert(i)
에 i 값이 부여되는 것이 아니라
결국 위에 있는 var i
에 접근하는 것이 때문에
반복문이 다 돌면서 i = nodes.length - 1
가 되고 나면
결국 모든 nodes
가 nodes.length
를 alert하게 된다.
이를 제대로 구현하려면 다음과 같이 해줘야 됨
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function ("i") {
return function (e) {
alert("i");
};
}(i);
}
};
다음과 같이 function
을 i
와 함께 묶어서 return하면
더 이상 var i
에 접근하는 것이 아닌, 별개의 i를 만들어
값만 전달하는 방식이 된다.
로그인 페이지를 짠다고 생각해보자.
로그인 버튼을 누르면 회원 정보를 db에서 조회해서 이를 띄워줘야한다.
request = prepare_the_request(); // 요청 메시지
response = send_request_synchronously(request); // 응답메시지 수령
display(response); // 응답 정보 표시
이렇게 서버의 정보를 받아다가 화면에 띄워주는 코드가 있다고 했을 때,
만약 서버의 응답이 시간이 즉각 되는 것이 아니라, 어느 정도의 시간이 소요된다면
response가 오기 전까지 cpu가 아무 일도 안하게 된다.
응답이 완료되었는지 알 방법도 없는데,
응답이 오기 전까지 다른 걸 하고 있을 수 있게끔 하는 방법은 없을까?
request = prepare_the_request();
send_request_asynchronously(request, function (response) {
display(response);
});
바로 위의 처럼 콜백을 이용하는 것이다.
function(response)
처럼 대게는 익명 함수로 전달되어서
응답이 완료된 이후에 실행할 함수를 콜백함수라고 한다.
이렇게 하면 당장 문제를 해결할 수는 있지만,
여러 번의 콜백이 필요한 경우에는 코드의 중첩 문제를 피할 수 없다.
따라서 promise
같은 개념이 등장함
모듈이란, 상태와 구현은 숨긴채 가져오는 함수나 객체를 말한다.
이를 이용하면, 자칫 잘못 사용할만한 글로벌 변수의 사용을 거의 없앨 수 있다.
왜냐면 모듈로 분리하면, 모듈만의 네임 스페이스를 갖기 때문이다.
또한, 내부 상태를 쉽게 건드릴 수 없는 객체를 만들고 싶을 때도 유용하다.
몇몇 메소드는 return value가 없다.
예를 들어, 그냥 상태만 변화시키고 종료되는 메소드는 아무것도 리턴하지 않음
만약 우리가 이런 메소드들에게 this
를 반환하도록하면 연속 호출이 가능하다.
getElement('myBoxDiv').
move(350, 150).
width(100).
height(100).
color('red').
border('10px outset').
padding('4px').
appendText("Please stand by").
on('mousedown', function (m) {
this.startDrag(m, this.getNinth(m));
});
}).
tip('This box is resizeable');
이런 식으로