[Effective JavaScript] 비표준 스택 검사 프로퍼티를 사용하지 마라

김범식·2023년 7월 10일
0
post-thumbnail

arguments.callee && arguments.caller




arguments.callee

javascript에서 사용되는 특별한 속성이다. 이 속성은 arguments 객체 내에서 현재 실행중인 함수에 대한 참조를 제공한다.

예를 들어 다음과 같은 재귀함수를 작성하다고 가정해보자

function factorial(n){
	if(n==0){
		return 1;
	} else {
		return n* arguments.callee(n-1);
	}
}

console.log(factorial(5)); //120

위 코드에서 factorial 함수 내부에 arguments.callee 를 사용하여 재귀적으로 함수를 호출했다. 이는 arguments.callee가 현재 실행중인 함수 factorial 에 대한 참조를 가지고 있기 때문에 함수이름을 명시하지 않고 자기자신을 호출할 수 있다.

한편 arguments.callee는 ES5의 strict 모드에서 사용을 금지하고 있으면 사용한다면 TypeError가 발생한다.



arguments.caller

arguments.callee 와 같이 ES5 strict모드에서는 사용이 금지되어 권장하지 않는다.

다음은 arguments.caller를 사용해서 함수를 호출한 함수에 대한 참조를 얻는 예시이다.

function outer(){
	inner()
}

function inner(){
	console.log(arguments.caller);
}

outer();

위의 예시에서 inner 함수 내부에서 arguments.caller를 사용하여 함수를 호출한 outer 함수에 대한 참조를 출력한다.



비표준 스택 검사 프로퍼티를 사용하지 말자


자바스크립트는 호출스택 즉 현재 실행되고 있는 활성 함수의 체인을 검사하기 위한 기능을 제공해 왔다. 그중 하나가 arguments.callee, arguments.caller 이다.

var factorial = (function(n){
	return (n <= 1) ? 1 : (n*arguments.callee(n-1);
});

arguments.callee로 현재 인자와 함께 호출한 함수를 참조하고 있다.

하지만 이는 특별히 좋은 방법이 아니다. 왜냐하면 함수 자신의 이름을 직접 참조하는 것이 더 간단하기 때문이다.

var factorial = (function(n){
	return (n <= 1) ? 1 : (n*arguments.callee(n-1);
});

arguments.caller는 주어진 인자 객체로 호출한 함수를 참조한다. 이 기능은 대부분의 환경에서 보안문제로 제거되었기 때문이다.

caller프로퍼티는 함수의 가장 최근의 호출자를 참조한다.

function revealCaller(){
	return revealCaller.caller;
}
function start(){
	return revealCaller()
}

start() === start; // ture;

이 프로퍼티를 사용하면 스택 추적, 즉 현재 호출 스택의 스냅샷을 보여주는 데이터 구조를 추출할 수 있다.

function getCallStack(){
	var stack=[];
	for(var f = getCallStack.caller; f; f = f.caller){ // 자신을 불러온 함수를 계속 찾아감
		stack.push(f);
	}
}

이 함수는 제대로 동작하는 것 처럼 보인다.

function f1(){
	return getCallStack();
}

function f2(){
	return f1();
}

var trace = f2();
console.log(trace) // [f1, f2]

하지만 이 getCallStack은 쉽게 오작동 할 수 있다. 호출 스택에 함수가 두번이 상 나타나면 스택 검사 로직은 반복문내에 갇히게 된다.

function f(n){
	return n === 0 ? getCallStack() : f(n-1);
}
var trace = f(1); //무한 루프 

무엇이 잘못되었냐 하면, 함수 f가 재귀적으로 자기 자신을 호출하기 때문에 caller프로퍼티는 자동으로 그 참조를 다시 f로갱신한다. 즉 나를 호출한 f가 아닌 다시 f를 가리켜서 내부 반복문 내에 영원히 갇히게 된다.

게다가 strict 모드에서는 명시적으로 callercallee를 허용하지 않고 있다.

function f(){
	"use strict"
	return f.caller;
}

스택을 조사하는 이유가 오로지 디버깅 이라면 인터랙티브 디버거를 사용하는게 더 좋다.



기억할 점

  • 신뢰할 만한 이식성을 보장하지 않은 비표준 arguments.caller, arguments.callee의 사용을 자제하자
  • 스택의 전체 정보를 제대로 포함하지 않은 함수의 비표준 caller 프로퍼티를 사용하지 말자
profile
frontend developer

0개의 댓글