[JavaScript] This

전홍석·2025년 7월 12일

javascript

목록 보기
4/11
post-thumbnail

This

JS에서는 함수 호출 방식에 의해 this에 바인딩할 객체를 동적으로 결정된다
즉, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 반인딩할 객체가 동적으로 결정된다

호출 방식

1. 함수 호출

일반적으로 Browser Side 에서 this 는 window, Server Side (node.js) 에서 this 는 전역 객체를 의미한다

기본적으로 this 는 전역객체에 바인딩되고, 전역함수와 내부함수에서도 this 는 전역객체에 바인딩된다

function foo() {
	console.log(this) // window
    function bar() {
    	console.log(this) // window
    }
}
foo()

메서드의 내부함수일 경우에도 this 는 전역객체에 바인딩된다

var value = 1

var obj = {
	value : 100
    foo : function () {
    	console.log(this) // obj
        console.log(this.value) // 100
        
        function bar() {
        	console.log(this) // window
        	console.log(this.value) // 1
        }
    }
}

obj.foo()

내부함수는 일반함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 this는 전역객체를 바인딩한다.
내부함수의 this 가 전역객체를 참조하는 것을 회피하는 방법은 아래와 같다

var value = 1

var obj = {
	value : 100
    foo : function () {
    	var that = this
    	console.log(this) // obj
        console.log(this.value) // 100
        
        function bar() {
        	console.log(this) // window
        	console.log(this.value) // 1
            
            console.log(that) // obj
        	console.log(that.value) // 100
        }
    }
}

obj.foo()


위 방법 외에도 apply, call, bind 메소드를 통해 this 를 명시적으로 바인딩할 수 있다

2. 메서드 호출

함수가 객체의 프로퍼티 값이면 메소드로서 호출되는데, 이때 메소드 내부의 this 는 해당 메소드를 소유한 객체, 즉 해당 메소드를 호출한 객체에 바인딩된다

var obj1 = {
	name : "Lee"
    sayName : function() {
    	console.log(this.name) 
    }
 }
 
 var obj2 = {
 	name : "Kim"
 }
 
 obj2.sayName = obj1.sayName
 
 obj1.sayName() // Lee
 obj2.sayName() // Kim


프로토타입 객체 메소드 내부에서 사용된 this도 일반 메소드 방식과 마찬가지로 해당 메소드를 호출한 객체에 바인딩된다

function Person(name) {
	this.name = name
}

Person.prototype.getName = function() {
	return this.name
}

var me = new Person("Lee")
console.log(me.getName()) // Lee

Person.prototype.name = "Kim"
console.log(Person.prototype.getName()) // Kim

3. 생성자 함수 호출

Js 에서 생성자 함수는 그 형식이 정해져 있는 것이 아니라 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다
따라서 혼란을 방지하기 위해 생성자 함수명은 첫문자를 대문자로 기술해야한다

생성자 함수 동작 방식

  1. 빈 객체생성 및 this 바인딩
    • 생성자 함수의 코드가 실행되기 전 빈객체가 생성되는데, 이 빈객체가 생성자 함수가 새로 생성하는 객체이다. 이후 생성자 함수 내에서 사용되는 this는 이 빈 객체를 가리킨다. 그리고 생성된 빈 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다
  2. this 를 통한 프로퍼티 생성
    • 생성된 빈 객체에 this 를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다. this 는 새로 생성된 객체를 가리키므로 this 를 통해 생성한 프로퍼티와 메소드는 새로 생성된 객체에 추가된다
  3. 생성된 객체 반환
    • 반환문이 없어도 this 에 바인딩된 새로 생성한 객체가 반환된다
function Person(name) {
	this.name = name
}

var me = new Person("Lee")
console.log(me.name) // Lee

객체 리터럴 방식과 생성자 함수 방식의 차이

객체 리터럴 방식과 생성자 함수 방식의 차이는 프로토타입 객체 ([[Prototype]]) 에 있다

  • 객체 리터럴 방식 : 생성된 객체의 프로토타입 객체는 Object.prototype
  • 생성자 함수 방식 : 생성된 객체의 프로토타입 객체는 (생성된 함수 객체).prototype

생성자 함수에 new 연산자를 붙이지 않고 호출할 경우

일반함수와 생성자 함수에 특별한 차이는 없으며 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다
그러나 객체 생성 목적으로 작성한 생성자 함수를 new 없이 호출하거나 일반함수에 new를 붙여서 호출하면 오류가 발생할 수 있다
일반함수를 호출하면 this는 전역객체에 바인딩되지만, new 연산자와 함께 생성자 함수를 호출하면 this 는 생성자 함수가 암묵적으로 생성한 빈 객체에 바인딩 된다

function Person(name) {
	this.name
}
var me = Person("Lee")

console.log(me) // undefined
console.log(window.name) // Lee

4. apply, call, bind 호출

this 에 바인딩될 객체는 함수 호출 패턴에 의해 결정되는데, 이는 JS 엔진이 수행한다
이러한 JS 엔진의 암묵적 this 바인딩 이외에 this를 특정 객체에 명시적으로 바인딩하는 방법도 제공되는데 이가 Function.prototype.apply, Function.prototype.call 메소드이다
이 메소드들은 모든 함수 객체의 프로토타입 객체인 Function.prototype 객체의 메소드이다

// thisArg : 함수 내부의 this에 바인딩할 객체
// argsArray : 함수에 전달할 argument의 배열
func.apply(thisArg, [argsArray])

var Person = function (name) {
	this.name = name
}

var foo = {}

Person.apply(foo, ["name"])
console.log(foo) // {name : "name"}

apply() 메소드의 대표적인 용도는 arguments 객체와 같은 유사배열 객체에 배열 메소드를 사용하는 경우이다

function convertArgsToArray() {
	var arr = Array.prototype.slice.apply(arguments) // arguments.slice()
    console.log(arr)
    return arr
}
convertArgsToArray(1, 2, 3) // [1, 2, 3]

Array.prototype.slice() 메소드를 arguments 객체 자신의 메소드인 것처럼 arguments.slice() 와 같은 형태로 호출한 것이다

apply() 메소드는 this를 특정 객체에 바인딩할 뿐 본질적인 기능은 함수 호출이다

call() 메소드의 경우 apply() 와 기능은 같지만 두번째 인자에서 각각 하나의 인자를 넘기게 된다

Person.call(foo, 1, 2, 3)

function Person(name) {
	this.name = name
}

Person.prototype.doSomeThing1 = function(callback) {
	if (typeof callback === "function") {
    	callback() // -----1
    }
}

Person.prototype.doSomeThing2 = function(callback) {
	if (typeof callback === "function") {
        callback.call(this) // -----1
    }
}

function foo() {
	console.log(this.name) // -----2
}

var p = new Person("Lee")
p.doSomeThing1(foo) // undefined
p.doSomeThing2(foo)	// Lee
  • doSomeThing1 : 1 시점에서는 this 는 Person 객체지만 2 시점에서 this 는 window가 된다
  • doSomeThing2 : 1 시점에서는 this 는 Person 객체이고, 2 시점에서도 this 는 Person 객체이다

Function.prototype.bind 를 사용하는 방법도 가능하다
bind 는 함수에 인자로 전달한 this 가 바인딩된 새로운 함수를 리턴한다
즉 bind는 apply, call 메소드와 같이 함수를 실행하지 않기 때문에 명시적으로 함수를 호출할 필요가 있다

function Person(name) {
	this.name
}

Person.prototype.doSomeThing = function(callback) {
	if (typeof callback === "function") {
    	callback.bind(this)()
    }
}

function foo () {
	console.log(this.name)
}

var p = new Person("Lee")
p.doSomeThing(foo) // Lee

참조
https://poiemaweb.com/js-this

profile
취뽀까지 숨참기

0개의 댓글