Hello,

kok nae-ga ha-myun an-dweneun MAGIC...🧚

개발 일지

[코딩테스트] Call Function with Custom Context (feat. this)

도담 🌱 2024. 8. 22. 12:08

최근 이직준비를 하며 코테를 풀어나가고 있는 도중, javascript에서 this의 개념에 대해 한번 더 생각하게 해준 문제를 하나 다뤄보려고 합니다. 해당 문제는 leetcode의 javscript - medium 레벨의 2693. Call Function with Custom Context 문제입니다. (아직 무료 버전을 사용중이라 디버깅을 node환경에서 돌려보고 있는점 참고부탁드립니다.)

.
.
.

/**
 * @param {Object} context
 * @param {Array} args
 * @return {null|boolean|number|string|Array|Object}
 */
Function.prototype.callPolyfill = function(context, ...args) {
    console.log(this); // A
    this();
}


function increment() { 
	console.log(this); // B
    
    this.count++; 
    return this.count; 
}
increment.callPolyfill({count: 1}); // 2

처음 마주한건 this의 개념이었는데요. callPolyfill 함수내에서 this에 값을 지정해주었었는데 B에서 찾을 수 없어서 확인 해 보니 💡아 맞다 this가 다르지.. 번뜩 떠올랐습니다. A와 B구간에서의 this는 어떤게 나오게 될까요? 우선 JS에서의 this는 다음과 같은 기준으로 정해지게 됩니다.

  • 전역공간 - window/global
  • 함수호출 - window/global
  • 메서드 호출 - 메서드 호출 주체
  • 생성자함수 호출 - 인스턴스
  • callback 호출 - 기본적으로는 함수와 동일 / 명시적 바인딩으로 변경 가능

this 부분은 이해보다는 암기..부분이라 생각되는데. 실제로 값을 확인해보면 A에서는 increment()가, B에서는 global객체가 this로 설정되어 있는 것을 확인 할 수 있습니다. 🤷‍♀️ 이 부분에서 increment의 this를 callPolyfill의 context를 넘어오는 걸 this로 잡아주어야겠다 생각하게 되었습니다.

.
.
.

Function.prototype.callPolyfill = function(context, ...args) {
    return this.apply(context, args);
}

사실 Function.prototype.call()이나, Function.prototype.apply()함수를 이용하여 명시적 바인딩을 사용하면 한줄로 간단하게 해결 가능한데요. 하지만 문제에서 해당 함수를 사용하지 말아달라 명시가 되어 있기에 다른 방법을 생각했어야 했습니다.

.
.
.

Function.prototype.callPolyfill = function(context, ...args) {
    const fnSymbol = Symbol(); // 고유한 Symbol 생성

    context[fnSymbol] = this; // 함수(this)를 context 객체에 메서드처럼 추가

    const result = context[fnSymbol](...args); // 함수 호출: this는 context가 됨

    delete context[fnSymbol];

    return result;
}

위에 설명했다 시피 메서드 호출시에는 this가 메서드 호출의 주체가 됩니다. 이를 이용하여 context의 객체 내에 this함수를 추가하여 저장해두고 context[fnSymbol]로 선언하여 메서드를 호출함으로써 this 문제를 해결하였습니다.

Symbol을 사용하지 않고 context.func = this; context.func() 이런식으로 작성해도 코딩테스는 문제에서는 별다른 오류는 없겠지만, Symbol로 고유한 값을 사용함으로써 혹시 모를 중복 명칭의 방지하고자, 안정성을 위해 사용해보았습니다.

.
.
.

[번외] 저는 제출 후 다른분들의 코드도 한번 확인해보는데요. 그중 아래와 같은 풀이가 있었습니다.

Function.prototype.callPolyfill = function (context, ...args) {
    const fn = this;
    Object.prototype.func = fn;
    return context.func(...args);
}

개인적으로는 전역 Object.prototype에 함수를 추가하는 방법이라 전역 object에 영향이 있을 수 있어 위험하다고 생각이 들었고, 이미 정의된 프로퍼티와 충돌할 가능성과 디버깅할때 찾기 힘들 수 있다고 생각이 들었습니다. 여러분들은 어떤 풀이로 해당 문제를 해결하셨나요? 😆

'개발 일지' 카테고리의 다른 글

SQL Injection 공격이 들어왔다 😵  (0) 2024.08.14