본문 바로가기
매일코딩/자바스크립트 개념

[자바스크립트 객체지향] 2 자바스크립트 함수가 독특한 이유

by 인생여희 2017. 6. 20.
반응형

함수

자바스크립트에서 함수는 객체이다. 다른 객체에는 없는 함수만의 특성을 꼽으라면 call이라는 내부 속성을 들 수 있다. 내부속성은 코드로 접근할 수 없지만 코드의 동작을 정의한다.

 

선언과 표현식

함수에는 두가지 리터럴 형태가 있다.


1.함수 선언

 

function add(num,num2){

 

return num1 + num2

}

 


2.함수 표현식

 

var add = function add(num,num2){

 

return num1 + num2

}


 *참고

위의 두 함수는 비슷하지만 한가지 중요한 차이점이 있다.함수선언은 코드가 실행될 때 컨텍스트 상단에 끌여올려진다다시 말해 함수를 호출하는 코드가 함수를 선언한 코드보다 앞에 있어도 에러가 발생하지 않는다는 뜻이다.

 

var result = add(5,5);

 

function add(num,num2){

 

return num1 + num2

}

 

함수 표현식은 변수를 통해서만 함수를 참조하기 때문에 호이스팅 되지 않는다.


var result = add(5,5);

 

var add = function add(num,num2){

 

return num1 + num2

}

 

값으로서의 함수

, 함수를 변수에 할당할 수도 있고 객체에 추가할 수도 있으며 다른 함수에 인수로 전달하거나 함수에서 함수를 반환 할 수도 있다.

 

function say(){

console.log("hi");

}

 

say() ; // hi

 

var say2 = say;

 

say2(); // hi 출력

say라는 함수를 만드는 함수 선언이 있다. 그 후에 만들어진 say2 라는 변수에 say의 값을 할당했다. saysay2는 이제 같은 함수를 가리킨다.

 

함수를 다른 함수에 인수로 전달할 수 도 있다.

 

var score = [4, 11, 2, 10, 3, 1];

 

/* 오류 */

score.sort(); // 1, 10, 11, 2, 3, 4

// ASCII 문자 순서로 정렬되어 숫자의 크기대로 나오지 않음

 

/* 정상 동작 */

score.sort(function(a, b) { // 오름차순

return a - b;

// 1, 2, 3, 4, 10, 11

});

 

인수

자바스크립트 함수의 다른 특성 중 하나는 인수를 몇 개 전달하든 에러가 발생하지 않는다는 것이다. 이는 함수의 인수가 실제로는 배열과 비슷한 arguments라는 구조체에 저장되기 때문이다. 평범한 자바스크립트 배열과 마찬가지로 arguments에 담을 수 있는 값은 개수 제한이 없다. 저장된 인수는 숫자 인덱스로 접근할 수 있으며 length 속성을 사용하면 저장된 인수의 개수를 알 수 있다.

함수 안에서 arguments 객체가 자동으로 만들어 진다. 

참고: arguments 객체는 Array의 인스턴스가 아니기 때문에 배열 메소드를 사용할 수 없다. Array.isArray(arguments)는 항상 false를 반환 한다.

 

<script>

//매개변수와 관련된 두가지 수가 있다.

// 하나는 함수.length

// 하나는 arguments.length

 

function zero(){

console.log(

'zero.length', zero.length,

'arguments', arguments.length

);

}

function one(arg1){

console.log(

'one.length', one.length,

'arguments', arguments.length

);

}

function two(arg1, arg2){

console.log(

'two.length', two.length,

'arguments', arguments.length

);

}

 

zero(); // zero.length 0 arguments 0

one('val1', 'val2'); // one.length 1 arguments 2

two('val1'); // two.length 2 arguments 1

</script>

 

 

오버로딩

대부분의 객체지향 언어는 한 함수에 여러 시그니쳐를 두는 함수 오버로딩을 지원한다. 함수 시그니처는 함수의 이름과 예상 인수 개수, 인수의 타입으로 이루어진다.

따라서 같은 함수라 해도 문자열 인수 한 개를 받는 시그니처와 숫자 인수 두 개를 받는 시그니처를 가질 수 있다.

하지만 자바스크립트 함수는 인수의 개수에 제한이 없으며 인수의 타입은 아예 정하지 않는다. 때문에 함수 오버로딩도 없다.

 

function say(msg){

console.log(msg);

}

 

function say(){

console.log("defalt msg");

}

 

say("hi") // defalt msg 출력

 

자바스크립트에서는 같은 이름을 가진 함수를 여러개 정의 하려고 할 때 마지막에 정의된 함수가 이긴다. 함수도 객체이기 때문에 두 번째에 있는 함수가 say에 재할당 되기 때문이다.

 

함수 오버로딩을 흉내낼 수 있는 방법

arguments 객체를 이용해서 함수에 전달된 인수의 개수를 구하고 이를 이용해 수행할 동작을 정의 하면 된다.

 

function say(msg){

if(arguments.length ===0 ){

msg = "defalt msg";

}

console.log(msg);

}

say("hi"); //hi 출력

 

say() 함수는 전달된 인수의 개수에 따라 다르게 동작한다. 전달된 인수가 없다면 (arguments.length ===0) 이 실행된다. 전달된 인수가 있을 때는 첫 번째 인수가 메시지로 사용된다. 타입까지 확인하고 싶다면 typeof 연산자와 intanceof 연산자를 이용한다.

  

객체 메소드

 

var person = {

name : "kim",

sayName: function(){

console.log(person.name);

}

 

}

 

person.sayName(); // kim 출력

 

this 객체

say() 메소드는 person.name을 직접 참조하고 있어서 객체와 메소드 간에 강한 결합이 만들어졌는데, 이는 문제의 소지가 많은 방법이다. 첫째, 변수 이름을 바꿀 때는 반드시 메소드 안에 있는 참조 코드도 바꿔줘야 한다. 둘째, 이런 식으로 강한 결합이 이루어지면 같은 함수를 여러 객체에 사용하기 어렵다.

 

문제 해결 방법은?

 자바스크립트의 모든 스코프에는 함수를 호출하는 객체를 뜻하는 this 객체가 있다. 전역 스코프에서 this는 전역 객체(웹브라우저에서는 window)를 참조한다. 객체에 붙어있는 함수를 호출하면 this의 값은 해당 객체가 된다. 따라서 메소드 안에서라면 객체를 직접 참조하는 대신 this를 참조할 수 있다.

 

var person = {

name : "kim",

sayName: function(){

console.log(this.name);

}

 

}

person.sayName(); // kim 출력

 

이제는 변수의 이름을 쉽게 바꿀 수 있으며 say() 함수를 다른 객체에 재사용하기도 쉬워졌다.

 

function sayAll(){

console.log(this.name);

}

 

// 두 객체 리터럴을 만들었고,

//sayNamesayAll 함수를 할당했다.

//함수는 참조값이므로 객체가 몇 개든 상관없이 객체의 속성으로

//함수를 할당할 수 있다.

 

var person1 = {

name : "kim",

sayName: sayAll

 

}

 

 

var person2 = {

name : "park",

sayName : sayAll

 

}

 

var name = "sim";

 

person1.sayName(); // kim

person2.sayName(); //park

 

sayAll(); //sim

 

 

this 값 변경

일반적으로 this 값은 자동으로 할당되지만 목적에 따라 바꿀수도 있다. 자바스크립트에서 this의 값을 바꿀 때 사용할 수 있는 함수 메소드는 세 종류가 있다. 함수도 객체이므로 메소드가 있다.

 

1.call 메소드

2.apply 메소드

3.bind 메소드

 

*call()

메소드는 this의 값을 바꾸는 것은 물론 인수도 전달하며 함수를 실행할 수 있다. call() 메소드의 첫 번째 인수는 함수를 실행할 때 this로서 사용될 값이다. 두 번째 인수부터는 함수를 실행할 때 그대로 전달된다. 다음 코드는 인수를 한 개 전달 받도록 한 것이다.

 

function say(label){

 

console.log(label+ ":"+ this.name);

}

 

var person1 = {

name: "kim"

}

 

 

 

var person2 = {

name: "park"

}

 

var name = "hong";

 

say.call(this,"global"); //global: hong 출력

say.call(person1,"person1"); //person1: kim 출력

say.call(person2,"person2"); //gperson2: park 출력

 

*apply() 메소드

apply() 메소드는 call()과 완전히 똑같이 동작하지만 인수를 두 개만 사용한다는 점이 다르다. apply() 메소드의 첫 번째 인수는 this로 사용할 값이고, 두 번째 인수는 함수에 전달할 인수를 담고 있는 배열 또는 배열과 유사한 객체이다. 즉 두 번째 인수로 arguments를 전달할 수도 있다.

 

function say(label){

 

console.log(label+ ":"+ this.name);

}

 

var person1 = {

name: "kim"

}

 

var person2 = {

name: "park"

}

 

var name = "hong";

 

say.applyl(this,["global"]); //global: hong 출력

say.apply(person1,["person1"]); //person1: kim 출력

say.apply(person2,["person2"]); //gperson2: park 출력

 

데이터가 이미 배열 형태로 존재한다면 apply()를 사용하는 편이 좋고 데이터가 개별 변수로 존재한다면 call()을 사용하는 편이 좋다.

 

요약

자바스크립트의 함수는 매우 특이하다. 함수도 객체이기 때문에 접근하고 복사하고 다시 정의할 수도 있고 다른 객체 값을 다루듯 사용할 수 있다. 자바스크립트에서 함수와 다른 객체의 가장 큰 차이점은 특수한 내부 속성 call 의 존재다. call에는 함수의 실행 동작이 정의되어 있다. typeof 연산자는 객체에 call 내부 프로퍼티가 있는지 찾아보고 있으면 function을 반환한다.

함수 리터럴 형식은 선언과 표현식이라는 두 종류가 있다. 함수 선언은 function 키워드 다음에 함수의 이름이 오며, 함수가 선언된 컨텍스트의 가장 위에서 정의된 것처럼 다루어진다. 함수 표현식은 평범한 값처럼 사용할 수 있어 할당 표현식, 함수 인수, 다른 함수의 반환 값등에 사용된다.

함수는 객체이기 때문에 function이라는 생성자가 존재한다. 자바스크립트에는 클래스라는 개념이 없기 때문에 함수 및 다른 객체만을 사용해 집합과 상속을 구현해야 한다.

 

 도서 객체지향 자바스크립트의 원리 정리

반응형

댓글