> 관련 글 :: [JS]자바스크립트 this(1/2)
🌈 자바스크립트 This
📘 생성자 함수 호출
1️⃣ 동작 방식
2️⃣ 객체 리터럴 방식과 생성자 함수 방식의 차이
3️⃣ new 연산자를 붙이지 않고 호출할 경우
📘 call / apply / bind 를 통한 호출
1️⃣ apply를 통한 바인딩과 함수 호출
2️⃣ apply와 유사 배열 객체
3️⃣ 콜백 함수와 apply, call
4️⃣ bind
📘 생성자 함수 호출
생성자 함수는 그 이름에 걸맞게 객체를 생성하는 역할을 한다.
또한, 함수에 new 연산자를 붙여 호출하면 해당 함수는 생성자 함수로써 호출되어 동작한다.
function Person(name) {
this.name = name;
}
let me = new Person('Lee');
console.log(me); // Person {name: 'Lee'}
new 연산자를 통해 생성자 함수를 호출하게 되면 this의 바인딩이 일반적인 함수 호출 때와는 다르게 동작한다.
function Person(name) {
this.name = name;
}
let you = Person('Kim');
console.log(you); // undefined
1️⃣ 동작 방식
new 연산자를 통해 생성자 함수를 호출하게 되면 다음과 같은 순서로 동작한다.
1. this가 바인딩되어있는 함수의 코드가 실행되기 전 객체가 생성된다.
이때의 객체는 생성자 함수로 새로 생성되는 객체이며, 생성자 함수 내에서 사용되는 this는 이 객체를 가리키게 된다. 그리고 생성된 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
2. this를 통한 프로퍼티가 생성된 객체에 this를 사용하여 동적으로 프로퍼티나 메서드를 생성할 수 있다.
this는 생성된 객체를 가리키므로 this를 통해 생성한 프로퍼티와 메서드는 새로 생성된 객체에 추가된다.
3. 생성된 객체에 return이 없는 경우 this에 바인딩된 객체가 리턴된다.
this가 아닌 다른 객체를 리턴하는 경우 this가 아닌 해당 객체가 반환되지만, 그러한 함수는 생성자 함수로서의 역할을 하지 못한다. 따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.
function Person(name) {
/* 1.생성자 함수 코드 실행 전 */
this.name = name; /* 2 */
/* 3.생성된 함수 반환 */
/* 주의! this가 아닌 다른것을 return하게되면 생성자함수가 아니게됨 */
}
let me = new Person('Lee');
console.log(me.name);
2️⃣ 객체 리터럴 방식과 생성자 함수 방식의 차이
객체 리터럴 방식으로 생성된 객체의 프로토타입 객체는 Object.prototype이다.
/* 객체 리터럴 방식 */
let foo = {
name: 'foo',
gender: 'male'
}
console.log(foo); // {name: 'foo', gender: 'male'}
생성자 함수 방식으로 생성된 객체의 프로토타입 객체는 Person.prototype이다.
/* 생성자 함수 방식 */
function Person(name, gender) {
this.name = name;
this.gender = gender;
}
let me = new Person('Lee', 'male');
console.log(me); // Person {name: 'Lee', gender: 'male'}
let you = new Person('Kim', 'female');
console.log(you); // Person {name: 'Kim', gender: 'female'}
3️⃣ new 연산자를 붙이지 않고 호출할 경우
일반 함수와 생성자 함수의 호출 시 this 바인딩 방식이 다르기 때문에,
생성자 함수를 new 없이 호출하거나 일반 함수에 new를 붙여 호출하면 오류가 발생할 수 있다.
◾ 일반 함수를 호출 시 this는 전역 객체에 바인딩된다.
◾ new 연산자로 생성자 함수를 호출하면 this는 생성자 함수가 생성한 빈 객체에 바인딩된다.
function Person(name) {
/* new없이 호출하는 경우, 전역객체에 name 프로퍼티를 추가 */
this.name = name;
};
/* 일반 함수로서 호출되었기 때문에 객체를 암묵적으로 생성하여 반환하지 않는다. */
/* 일반 함수의 this는 전역객체를 가리킨다. */
let me = Person('Lee');
console.log(me); // undefined
console.log(window.name); // Lee
일반 함수와 생성자 함수에 형식적 차이가 없기 때문에 일반적으로 생성자 함수의 첫 글자를 대문자로 한다.
또한, 일반 함수와 생성자 함수의 혼동을 피하기 위해 사용되는 패턴(Scope-Safe Constructor)은 아래와 같다.
/*
* Scope-Safe Constructor Pattern
*
* this가 호출된 함수(arguments.callee, 본 예제의 경우 A)의 인스턴스가 아니면 new 연산자를
* 사용하지 않은 것이므로 new와 함께 생성자 함수를 호출하여 인스턴스를 반환한다.
* 이 예제의 경우 A로 표기하여도 문제없이 동작하지만 특정함수의 이름과 의존성을 없애기 위해서
* arguments.callee를 사용하는 것이 좋다.
*/
function A(arg) {
/* arguments.callee는 호출된 함수의 이름을 나타낸다. */
if (!(this instanceof arguments.callee)) {
return new arguments.callee(arg);
}
// 프로퍼티 생성과 값의 할당
this.value = arg ? arg : 0;
}
let a = new A(100);
let b = A(10);
console.log(a.value); // 100
console.log(b.value); // 10
📘 call / apply / bind 를 통한 호출
this에 바인딩될 객체는 함수 호출 패턴에 의해 결정되며 자바스크립트 엔진에 의해 암묵적으로 수행된다.
명시적으로 this를 바인드 하는 방법에는 Function.prototype.apply, Function.prototype.call 메서드를 통한 방법이 있다. 이 메서드들은 함수 객체의 프로토타입 객체인 Function.prototype의 메서드이다.
1️⃣ apply를 통한 바인딩과 함수 호출
/*
* thisArg: 함수 내부의 this에 바인딩할 객체
* argsArray: 함수에 전달할 argument의 배열
*/
func.apply(thisArg, [argsArray])
중요한 것은, apply()는 함수에 의해 호출되며 this를 특정 객체에 바인딩할 뿐, 주 기능은 함수 호출이라는 것이다.
let Person = function (name) {
this.name = name;
};
let foo = {};
/* apply()는 생성자함수 Person을 호출하고 this에 foo를 바인딩한다. */
Person.apply(foo, ['name']);
console.log(foo); // { name: 'name' }
위 코드의 동작 과정은 다음과 같다.
1. 객체 foo를 apply()의 첫 번째 매개변수로, argument의 배열을 두 번째 매개변수로 전달하여 Person 함수를 호출한다.
2. 이때, Person 함수의 this는 foo 객체가 된다.
3. Person 함수는 this의 name 프로퍼티에 매개변수 name에 할당된 인수를 할당한다.
4. this에 바인딩된 foo 객체에는 name 프로퍼티가 없으므로 name 프로퍼티가 동적으로 추가되며 값이 할당된다.
2️⃣ apply와 유사 배열 객체
arguments 객체와 같은 유사 배열 객체는 배열이 아니기 때문에 slice( )와 같은 배열의 메서드를 사용할 수 없으나 apply( ) 메서드를 이용하면 배열의 메서드를 사용할 수 있게 된다.
/*
* arguments 객체를 배열로 변환
* slice: 배열의 특정 부분에 대한 복사본을 생성한다.
*/
function convertArgsToArray() {
console.log(arguments);
let arr = Array.prototype.slice.apply(arguments);
// 위의 코드는 let arr = [].slice.apply(arguments); 와 같다.
console.log(arr);
return arr;
}
convertArgsToArray(1, 2, 3);
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// (3) [1, 2, 3]
위 코드의 동작 과정은 다음과 같다.
1. Array.prototype.slice.apply(arguments): Array.prototype.slice() 메서드를 호출, this는 arguments 객체 바인딩
2. Array.prototype.slice()를 arguments 객체 자신의 메서드인 것처럼 arguments.slice()와 같은 형태로 호출
call()의 경우, apply()와 기능은 같지만 apply()의 두 번째 인자에서 배열 형태로 넘긴 것을 각각 하나의 인자로 넘긴다.
Person.apply(foo, [1, 2, 3]);
Person.call(foo, 1, 2, 3);
3️⃣ 콜백 함수와 apply, call
apply( )와 call( )는 콜백 함수의 this를 위해서 사용되기도 한다.
function Person(name) {
this.name = name;
}
Person.prototype.doSomething = function(callback) {
if(typeof callback == 'function') {
// --------- 1
callback();
}
};
function foo() {
console.log(this.name); // --------- 2
}
let p = new Person('Lee');
p.doSomething(foo); // undefined
1. 1의 시점에서 this는 Person 객체이지만, 2의 시점에서 this는 전역 객체 window를 가리킨다.
2. 콜백 함수를 호출하는 외부 함수의 this와 콜백 함수의 this가 다르기 때문이다.
3. 따라서 콜백 함수의 this를 콜백 함수를 호출하는 함수의 this와 일치시켜 주어야 한다.
function Person(name) {
this.name = name;
}
Person.prototype.doSomething = function (callback) {
if (typeof callback == 'function') {
callback.call(this);
}
};
function foo() {
console.log(this.name);
}
var p = new Person('Lee');
p.doSomething(foo); // 'Lee'
4️⃣ bind
bind는 ES5에서 추가되었으며, 함수에 인자로 전달한 this가 바인딩된 새로운 함수를 리턴한다.
bind는 apply, call과 같이 함수를 실행하지 않기 때문에 명시적으로 함수를 호출해야 한다.
function Person(name) {
this.name = name;
}
Person.prototype.doSomething = function (callback) {
if (typeof callback == 'function') {
// callback.call(this);
// this가 바인딩된 새로운 함수를 호출
callback.bind(this)();
}
};
function foo() {
console.log('#', this.name);
}
let p = new Person('Lee');
p.doSomething(foo); // 'Lee'
📌 참고사이트
'Frontend > JavaScript' 카테고리의 다른 글
[JS] innerHTML과 InsertAdjacentHTML 비교(DOM Element) (0) | 2022.08.22 |
---|---|
[JS] 자바스크립트 this (1/2) (0) | 2022.06.08 |
[JS] 참조 타입의 얕은 복사와 깊은 복사(Shallow Copy & Deep copy) (2) | 2022.05.25 |
[JS]자바스크립트의 원시 타입과 참조 타입 (0) | 2022.05.24 |
[JS] 자바스크립트 큰 수 다루기 (BigInt) (0) | 2022.05.20 |
최근댓글