"함수호출과 자바스크립트의 this정체"에 이은 두 번째 글이다.
지난 글 "함수호출과 자바스크립트의 this정체"에서 this가 함수 호출식에 따라 객체를 가리켰다면, 오늘 다룰 call,apply,bind는 직접 함수가 실행문맥을 결정한다. 외부에서 할당하는 this가 함수의 실행문맥이다.
함수가 실행되는 동시에 this를 정하는 call,apply,bind 에 대해 이야기해 보자
[실행 컨텍스트(실행문맥)과 생성자 함수호출 부분만 설명하면 "함수의 호출" 부분은 어느 정도 다뤘다고 생각된다.]
객체(this)는 함수가 실행되는 "기반", "영역","맥락",을 의미하며 이것을 실행하는 "주체"를 의미한다.이런 이유로 어떻게 this가 설정되는지 알아야 한다.
호출식에 따라 내부 this가 설정되는 반면 call,apply,bind 메서드는 사용자가 직접 외부 this를 넣는 것으로 작동한다.
함수를 호출하는 방식에는
- 함수이름 뒤 ( );
- 함수이름.call/apply.bind(객체, 인수/[인수])
이렇게 2가지가 있다.
call,apply,bind에 대해서 알아보자.
우선 함수가 무엇인지 알아보자.
함수는 함수객체라고 불린다. 객체는 (변수)프로퍼티와 함수형태인 프로퍼티를 갖는데 여기서 함수인 프로퍼티를 메서드라고 부른다. 객체는 .(점 연산자)를 통해 자신의 프로퍼티인 메서드를 사용할 수 있다.
call,apply,bind는 함수의 프로퍼티(메서드)다
이런 이유로 다음과 같이 call,apply,bind를 사용 할 수 있다.
함수이름.call() , 함수이름.apply() , 함수이름.bind()
그 이유는 부모인 Function.Prototype으로 부터 call,apply,bind을 물려받았기 때문이다. 자바스크립트는 프토토타입을 기반의 언어로 객체를 상속하는 언어이다. 그래서 모든 함수(function)는 call,apply,bind(프로퍼티)를 호출 할 수 있다.
call,apply,bind의 처음과 끝을 마무리하는 특징이다.
(일반)함수가 호출형태에 따라 this가 달라진다 반면에,call,apply,bind메서드는 외부에서 할당하는 첫번째 인자가 그 함수의 this가 된다. 함수 자신의 실행"환경"을 외부 this로 설정할 수 있는 것이 주요한 특징이다.
"일반 함수로도 호출 할 수 있는 걸 굳이 call,apply까지 사용했을까"
그것의 이유는 단연 call,apply메서드의 첫번째 인수 때문이다.
call,apply는 첫번째 인수 없이도 에러 없이 작동한다.
MDN(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/call) 을 읽어보면
인수가 없을 경우, 자동적으로 전역객체를 가리키게 된다.
fun.call(thisArg[, arg1[, arg2[, ...]]])
위 수식은,
fun 이라는 함수를 call,호출하는데, 어디[this(여기에서는thisArg)]에서 호출하는지 설명하는 수식이다.
var someFunc = function(){
return this.length
}
라는 함수가 있을 때,
someFunc.call([1,2,3])이라고 하면 3이 반환된 걸것이다.
마찬가지로 someFunc.apply([1,2,3])할 경우 3이 나온다.
즉 이를 보기 편하게 한다면
[1, 2, 3].someFunc(); //3
일 것이다.
(*apply는 함수인자를 배열로 전달하는 차이만 있을 뿐이다.)
다음은 인수를 여러개 받을 수 있는 함수를 합성하는 함수의 정의다 [모던자바스크립트 입문, 길벗 p311]
함수f와 함수g를 합성하는 compose 함수이다.
이때, call과 apply를 주목하자,
빨간색의 call은 g이하 부분을 한 덩어리를 인자로 넣고
파란색의 apply는 arguments(배열인자)를 인자로 넣고 있다.
그리고 각 this로 f와 g의 실행문맥을 지정했다.
세부적으로 생각해보자.
"f.call"은 f함수를 실행한다는 의미고,
"g.apply"는 g함수를 실행한다는 것이다.
그리고 "this"가 있는데, 여기 this는 익명함수를 가리킨다.
(*확실하지는 않지만 그렇게 추측하고 있다.아는 분은 무엇을 가리키고 있는지 댓글 부탁한다:] )
call을 이용한 함수호출과 상속파트에서 call이 사용된 예를 설명하겠다. 단 함수를 빌려오거나 프로퍼티를 가져오는 것은 call함수를 사용한 결과의 표현이지, call메서드의 역할이 "가져오는" 의미는 아니라고 생각한다
블로그(http://webclub.tistory.com/394)를 바탕으로 개인의견을 더해 작성했다.
함수는 객체를 "바탕"으로 작동하는데, 어떤 새로운 함수를 사용하려면 반드시 이미 사용 중인 객체에서 함수를 실행해야된다고 본다. 다시 말해, 서로 같은 객체에서 함수를 실행해야 자신이 원하는 값을 얻을 수 있다.
이런 맥락에서 만약 사용하려는 함수가 자신의 객체에 선언(정의)되어있지 않다면, call 메서드를 이용해 이를 해결 할 수 있다.
이렇게 말이다.
func. call(obj)
사용하고 싶은 함수. call(현재 객체)
결과적으로, 함수를 호출(call)되며, 이 함수는 현재 객체에 바인딩 된다.
상속은 자식이 부모의 것을 갖는 개념이라고 본다.
자식은 부모의 것을 받는데, 몇개의 상속방법 중 생성자에서 다른 생성자를 call 하는 방식이 메모리 절감에 도움된다 한다.
자식 생성자"안에서" 부모 생성자의 프로퍼티를 가져올 경우
call 메서드를 사용한다.
부모생성자. call(this, 부모 프로퍼티);
이때
1.call의 역할은 무엇일까
2.this는 무엇을 가리킬까?
3.서로 어떻게 작동할까?
1. call의 역할은 무엇일까
부모생성자의 프로퍼티 부분을 실행한다.
2.this는 무엇을 가리킬까?
선언된 this는 자식 생성자 범위(블록{..})안에 설정됐기 때문에, 자식 생성자로 만들어진 객체를 가리킨다.
3.서로 어떻게 작동할까?
이것이 어떻게 프로퍼티를 가져온다고 이야기 할 수 있을까?
이를 위해서 new 생성자 과정을 이해 해야한다.
생성자가 new 연산자로 호출되면 우선 빈 객체({ })가 생성되고
그 빈 객체에 this가 바인딩이 된다. 이때 this가 해당 생성자의 객체가 된다.
객체가 생성된 후,
자식생성자를 실행할 차례다.
"부모생성자. call(this, 부모 프로퍼티); 가 실행 되면
부모 생성자의 "this.변수" 부분을 호출한다.
"this.변수"는 객체(this)에 변수가 설정되는 구문이다.
원래 this가 부모 생성자를 가리켰지만 call()메서드에서 this를 자식 생성자로 설정했기 때문에,
"부모객체.프로퍼티" 가 아니라, "자식객체.프로퍼티" 로 변경이 된다.
this가 변경되는 역할을 call의 첫번째 인수가 맡은 것이다.
다시 말해 call 함수가 부모 생성자를 호출하는 동시에
객체를 자식 생성자 객체의 this로 설정했다.
이것이 call의 동작과정이다.
Array.prototype의 slice 메서드를 "빌려"와 일반 리터럴 객체/arguments에 연결한 형태이다.
왜 call을 사용했을까?
세부적인 과정을 설명하면,
Array.prototype.slice.call/apply
일반 리터럴 객체/arguments에는 배열 메서드가 없다. 없는 것을 호출 할 수는 없다.
Array.prototype을 호출(call)하면 Array Objet에 접근할 수 있다. 이를 통해 배열 메서드를 사용 "할 수 있다."
지금은 slice 메서드를 가져온 것이다.
Array.prototype.slice.call/apply.(obj/*arguments)
그럼 slice 메서드가 실행되는 기반은 어디인가? slice 메서드는 obj 객체 혹은 arguments 객체에서 사용된다.
(*arguments는 함수인자로 들어온 값을 모아둔 객체이다. 배열처럼 생긴 객체를 말한다. 배열이 아니다, 객체다)
추구하는 목적은 같으나 용도는 서로 다르다.
call,apply는 함수를 실행해서 그 함수의 값을, bind 지정한 객체의 새로운 함수를 만든다.
쉽게 말해, call,apply는 그냥 함수가 실행되도록 "도와"주는 것이고 bind 는 "새로운" 함수를 "만들어" 준다.
call, apply : 함수가 반환하는 값(value)
bind : "새로운 문맥(new context)를 가진 함수(function)
형태의 차이이지 의미의 차이는 아니다.인수로 전달하는 형태가 서로 다르다. call은 인자를 하나하나씩 전달하지만 apply는 배열로 인자를 전달한다.
안녕하세요, 글 잘 읽었습니다~! 감사합니다.
내용 중에 case -1 call 부분은 case-1 apply여야할 것 같습니다. 오타 같네요..!
그리고 compose 설명 부분 this 관련 설명은 익명 함수라기보단 compose 함수를 호출하는 문맥을 바인딩하는 코드라고 봐야 맞지않을까요!?