본문 바로가기
프로그래밍/자바스크립트

자바스크립트 함수(Function)와 함수의 call(), apply(), bind() 함수

by pentode 2018. 4. 10.

자바스크립트의 함수(Function)는 실제로는  전역객체의 함수 입니다. 전역객체는 웹브라우저일 경우 window이고, Node.js일 경우 global이 됩니다. 다음과 같이 일반적인 전역 함수를 정의하고 실행할 수 있습니다.

 

function add(a, b) {
	return a + b;
}

console.log( add(3, 5) );

결과)
8

 

이 add() 함수는 다음과 같이 호출할 수 있습니다.

 

window.add(3, 5);

결과)
8

 

결과는 동일합니다. 실제로 두 함수는 동일한 함수 입니다.

 

 

※ 자바스크립트 함수는 그 자체로 객체입니다. 자바스크립트는 동적언어라 객체에 나중에 속성을 추가하는 것이 가능합니다.

 

function bar() {
    console.log( "bar..." );
}

bar.a = 5;
console.log( bar.a );   // 5


bar.prototype.a = 4;
console.log( bar.a );  // 5

var obj = new bar();   // bar...
console.log( obj.a );  // 4

결과)
5
5
bar...
4

 

함수인 bar() 에 속성을 추가할 수 있는 것을 알 수 있습니다. 예제에 추가되어 있는 prototype은 이글에서는 자세히 다루지 않겠습니다.

 

 

※ 객체의 속성(property)으로써 호출되는 모양을 보겠습니다. 리터럴 표기법으로 객체를 정의합니다.

 

var koGreeting = {
	greeting : "안녕",
	name : "홍길동",
	sayHello: function() {
		return this.greeting + ", " + this.name;
	}
};

console.log(koGreeting.sayHello());

결과)
안녕, 홍길동

 

여기서 함수 sayHello()는 객체 koGreeting 의 속성(property) 입니다.

 

객체 참조를 생략한 전역함수는 window 객체의 속성(property)이 되는 것입니다. 이것으로 자바스크립트의 모든 함수는 객체의 속성(property)임을 알 수 있습니다. 또한 함수 자체도 객체입니다.

 

 

※ 함수 내에서 사용되는 this 키워드는 그 함수를 호출하고 있는 위치에 따라 this가 참조하는 객체가 바뀝니다.

 

// 함수를 전역으로 선언합니다.
function plus(y) {
    console.log(this.x + y);
}

var x = 5;   // window.x

// this.x 는 위의 전역변수 x가 됩니다.
plus(5);     // 10


// 객체를 하나 만들고, plus함수를 속성으로 넣어줍니다.
var obj = {
    x: 10,
    add: plus // 위의 plus 함수를 속성으로 준다.
};

// 객체의 속성으로  호출되므로 this.x 는 obj.x가 됩니다.
obj.add(5);   // 15


// 역으로 객체를 하나 만들고, 객체내에서 속성 함수를 정의했습니다.
var foo = {
    x: 10,
    add: function (y) {
        console.log( this.x + y );
    }
}

// foo객체의 속성함수로 add를 호출하면 this.x는 foo.x가 됩니다.
foo.add(5); // 15

// foo의 속성 함수의 참조를 전역으로 가져옵니다.
var add = foo.add;

// 외부로 가져와 실행하면 this.x 는 windows.x 가 됩니다.
add(5);    // 10


결과)
10
15
15
10

 

 

 

※ 함수의 또 다른 사용법으로 객체의 생성자로 사용될 수 있습니다.

 

 

function Greetings() {
	this.greeting = "안녕";
	this.name = "홍길동";
	console.log(this);
}

Greetings();
var g = new Greetings();

결과)
Window {stop: function, open: function, alert: function, confirm: function, prompt: function…}
Greetings {greeting: "안녕", name: "홍길동"}

 

이 함수 Greetings()가 직접 호출되면 그냥 함수로 사용됩니다. new Greetings()로 사용되면 Greetings 객체의 생성자로 사용이 되어집니다.

 

new Greetings() 로 생성되는 객체에 속성함수를 추가 하기 위해서는 다음과 같이 prototype 에 직접 추가 하는 방법을 사용합니다.

 

function Greetings() {
	this.greeting = "안녕";
	this.name = "홍길동";
}

Greetings.prototype.sayHello = function() {
	return this.greeting + ", " + this.name;
};

var g = new Greetings();
console.log(g.sayHello());

결과)
안녕, 홍길동

 

 

※ 앞에서 함수는 객체이고, 자신의 속성을 가지고 있으며, 호출되는 위치에 따라 this 참조가 변한다는것을 알았습니다. call(), apply(), bind() 함수는 함수객체가 기본으로 가지는 함수 입니다.

세 함수는 모두 함수에서 this로 사용할 객체를 명시적으로 지정하여 실행되도록 해줍니다.

call() 함수는 함수객체에 미리 정의되어 있는 함수로 첫번째 인자로 객체를 주는데 call()을 호출하는 함수가 인자로 주어진 객체에 딸려 있는 객체 처럼 동작하게 합니다. 사용방법은 fun.call(thisArg[, arg1[, arg2[, ...]]]) 와 같습니다.

첫 번째 인자는 함수를 소유하는 객체를 지정하고,  두 번째 부터는 옵션으로 필요하다면 함수로 보내는 인자를 지정할 수있습니다. 예를 보겠습니다.

 

var koGreeting = {
	greeting : "안녕",
	name : "홍길동",
	sayHello: function() {
		return this.greeting + ", " + this.name;
	}
};

var enGreeting = {
	greeting: "Hello",
	name: "HongKilDong"
};

console.log(koGreeting.sayHello());
console.log(koGreeting.sayHello.call(enGreeting));

결과)
안녕, 홍길동
Hello, Hong KilDong

 

koGreeging객체의 sayHello()의 call() 함수에 첫 번째 인자로 enGreeting 객체를 주었습니다. call() 함수는 sayHello()가 koGreeting 객체가 아닌 첫 번째 인자로 주어진 enGreeting 객체를 this로 사용할 객체로 동작하게 해줍니다.

이 예제에서는 koGreeting 객체내에 sayHello 함수를 선언 했지만, 전역 함수도 동일하게 사용할 수 있습니다.

 

var greetings = [
	{ greeting: '안녕', name: '홍길동' },
	{ greeting: 'Hello', name: 'KDHong' }
];

for (var i = 0; i < greetings.length; i++) {
	(function(i) {
		this.print = function() {
			console.log('#' + i + ' ' + this.greeting + ', ' + this.name);
		}
		this.print();
	}).call(greetings[i], i);
}

결과)
#0 안녕, 홍길동
#1 Hello, KDHong

 

이 예제에서는 익명함수의 call() 메소드를 호출하는데, 첫 번째 인자는 greetings 객체 배열의 각 요소를 주고, 두 번째 인자는 함수의 인자로 보낼 인덱스를 주게 됩니다.

 

 

※ apply() 메소드는 call() 메소드와 기능은 동일합니다. 차이점은 인자로 객체와 인자배열을 받는다는 것입니다.

 

function foo(lang, name) {
    if(lang === "ko") {
        console.log(this.koHello + name);
    } else {
        console.log(this.enHello + name);
    }
}

var greeting = {
    koHello: "안녕 ",
    enHello: "Hello ",
};


foo.call(greeting, "ko", "홍길동");
foo.apply(greeting, ["en", "HongGilDong"]);

결과)
안녕 홍길동
Hello HongGilDong

 

call(), apply()는 첫번째 인자로 this 로 사용할 객체를 받고, 두 번째 부터는 원래 함수에서 사용할 인자를 받게 됩니다.

 

 

※ bind() 함수는 인수로 주어진 객체를 this로 사용하는 함수를 반환합니다. 반환된 함수 호출시 줄 인자중 일부를 미리 설정할수도 있습니다.

 

function foo(lang, name) {
    if(lang === "ko") {
        console.log(this.koHello + name);
    } else {
        console.log(this.enHello + name);
    }
}

var greeting = {
    koHello: "안녕 ",
    enHello: "Hello ",
};

// greeting을 this로 사용하는 함수를 반환합니다.
var baz = foo.bind(greeting);
baz("en", "HongGilDong");

// 인자의 일부를 미리 셋팅할 수 있습니다.
var bar = foo.bind(greeting, "ko");
bar("홍길동");

결과)
Hello HongGilDong
안녕 홍길동


※ this와 prototype에 대한 자세한 이해가 필요하지만 이 글의 범위를 벗어나는것 같습니다.  글의 내용을 정리하자면 다음과 같습니다.

1. 함수는 그 자체로 객체입니다.
2. call(), apply(), bind()는 함수가 가지는 속성입니다(실제로는 함수의 prototype이 가지는 속성임).
3. 함수내에서 사용되는 this는 함수가 호출되는 위치에 의해서 정해집니다.
4. call(), apply(), bind()를 사용하면 this로 사용될 객체를 명시적으로 지정할 수 있습니다.

반응형