this
this
는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조변수다.this
를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메소드를 참조할 수 있다.
자바스크립트의 this는 다른 언어와는 다르게 함수 호출 방식에 의해 this에 바인딩할 객체가 동적으로 결정됩니다. 즉 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정됩니다.
- 함수 호출
function foo() {
console.log("foo's this: ", this); // window
function bar() {
console.log("bar's this: ", this); // window
}
bar();
}
foo();
기본적으로 this는 전역 객체에 바인딩됩니다. 전역함수는 물론이고 내부함수의 경우에도 전역객체에 바인딩됩니다.
또한, 메서드의 내부함수, 콜백함수의 내부함수의 경우 등... 내부함수는 어디에서 선언되든간에 전역객체에 바인딩됩니다. (API 통신처리를 할 때 콜백함수로 화살표함수를 사용하지 않으면 this로 메서드를 사용할 수 없는 이유를 여기서 찾았다.)
- 메소드 호출
var obj1 = {
name: 'Lee',
sayName: function() {
console.log(this.name);
}
}
var obj2 = {
name: 'Kim'
}
obj2.sayName = obj1.sayName;
obj1.sayName();
obj2.sayName();
함수가 객체의 프로퍼티 값이면 메소드로서 호출되는데 이 때 메소드 내부의 this는 메소드를 소유한 객체에 바인딩됩니다.
- 생성자 함수 호출
생성자 함수가 생성하는 객체로 this가 바인딩 됩니다. 하지만 new 키워드를 빼먹는 순간 일반 함수 호출과 같아지므로 이 경우는 this가 window에 바인딩됩니다.
function Person(name) {
this.name = name;
}
var kim = new Person('kim');
var lee = new Person('lee');
console.log(kim.name); //kim
console.log(lee.name); //lee
var name = 'window';
function Person(name) {
this.name = name;
}
var kim = Person('kim');
console.log(window.name); //kim
function
함수란 어떤 작업을 수행하기 위해 필요한 문들의 집합을 정의한 코드 블록입니다. 함수의 일반적인 기능은 어떤 작업을 수행하는 문들의 집합을 정의하여 코드의 재사용에 목적이 있습니다. 이러한 기능외에도 객체 생성, 객체의 행위 정의(메서드), 정보 은닉, 클로저, 모듈화 등의 기능을 수행할 수 있습니다.
정의 방식
- 함수 선언문
// 함수 선언문
//함수명, 매개변수 목록, 함수 몸체
function square(number) {
return number * number;
}
- 함수 표현식
자바스크립트는 일급 객체이므로 아래와 같은 특징이 있습니다.
- 무명의 리터럴로 표현이 가능하다.
- 변수나 자료 구조(객체, 배열…)에 저장할 수 있다.
- 함수의 파라미터로 전달할 수 있다.
- 반환값(return value)으로 사용할 수 있다.
이러한 특징 때문에 아래와 같이 표현식으로 사용할 수 있습니다.
// 기명 함수 표현식(named function expression)
var foo = function multiply(a, b) {
return a * b;
};
// 익명 함수 표현식(anonymous function expression)
var bar = function(a, b) {
return a * b;
};
console.log(foo(10, 5)); // 50
console.log(multiply(10, 5)); // Uncaught ReferenceError: multiply is not defined
- Function 생성자 함수
함수 선언문과 함수 표현식은 모두 함수 리터럴 방식으로 함수를 정의하는데 이것은 결국 내장 함수 Function 생성자 함수로 함수를 생성하는 것을 단순화 시킨 축약법입니다.
new Function(arg1, arg2, ... argN, functionBody)
var square = new Function('number', 'return number * number');
console.log(square(10)); // 100
함수 호이스팅
var res = square(5);
function square(number){
return number * number;
}
코드를 보면 함수 선언식으로 함수가 정의되기 이전에 함수 호출이 가능합니다. 함수 선언문의 경우, 함수 선언의 위치와는 상관없이 코드 내 어느 곳에서든지 호출이 가능한데 이것을 함수 호이스팅이라고 합니다.
var res = square(5); // TypeError: square is not a function
var square = function(number) {
return number * number;
}
함수 선언문과는 달리 에러가 발생합니다. 함수 표현식의 경우 함수 호이스팅이 아니라 변수 호이스팅이 일어납니다. 변수 호이스팅은 실제로 undefined로 초기화되고 실제 할당은 할당문에서 이뤄지기 때문에 에러가 납니다.
함수 객체의 프로퍼티
자바스크립트의 함수는 객체이기 때문에 프로퍼티를 가질 수 있습니다.
function square(number) {
return number * number;
}
square.x = 10;
square.y = 20;
console.log(square.x, square.y);
arguments 프로퍼티
arguments 프로퍼티는 함수 호출 시 전달된 인수들의 정보를 담고 있는 유사 배열 객체입니다.
- 매개변수의 갯수보다 인수를 적게 전달했을 때 전달되지 않은 변수는
undefined
로 초기화됩니다. - 매개변수의 갯수보다 인수를 더 많이 전달했을 때, 초과된 인수는 무시됩니다.
그 외에도 caller, length, name 같은 프로퍼티가 존재합니다.
형태
- 즉시 실행 함수 : 함수의 정의와 동시에 실행되는 함수를 즉시 실행 함수라고 합니다.
- 내부 함수 : 함수 내부에 선언한 함수입니다.
- 재귀 함수 : 자기자신을 호출하는 함수입니다.
- 콜백 함수 : 명시적으로 호출하는 방식이 아니라 특정 이벤트가 발생했을 때 시스템에 의해 호출되는 함수를 말합니다.
scope
스코프는 코드에서 유효한 범위를 의미합니다. 자바스크립트에서는 크게 2가지로 나눌 수 있습니다.
- 전역 스코프 : 코드 어디서든지 참조가능
- 지역 스코프 : 함수 코드 블록이 만든 스코프로 함수 자시과 하위 함수에서만 참조 가능
또한 자바스크립트에서는 c언어와 다르게 함수 레벨 스코프를 따릅니다. 이는 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다는 것입니다. (let으로 블록 레벨 스코프 사용 가능)
렉시컬 스코프
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
위의 코드를 보면 자바스크립트에서는 어디에서 선언하였는지에 따라 상위 스코프를 결정하는 방식을 따릅니다. 이러한 방식을 렉시컬 스코프라고 합니다.
암묵적 전역
var x = 10; // 전역 변수
function foo () {
// 선언하지 않은 식별자
y = 20;
console.log(x + y);
}
foo(); // 30
foo 함수가 호출되면 자바스크립트 엔진은 변수 y에 값을 할당하기 위해 먼저 스코프 체인을 통해 선언된 변수인지 확인한다. 이때 foo 함수의 스코프와 전역 스코프 어디에서도 변수 y의 선언을 찾을 수 없으므로 참조 에러가 발생해야 하지만 자바스크립트 엔진은 y = 20
을 window.y = 20
으로 해석하여 프로퍼티를 동적 생성한다. 결국 y는 전역 객체의 프로퍼티가 되어 마치 전역 변수처럼 동작한다. 이러한 현상을 암묵적 전역(implicit global)이라 한다.
prototype
자바스크립트의 모든 객체는 자신의 부모 역할을 담당하는 객체와 연결되어 있습니다. 이것은 마치 상속 개념과 같이 부모 객체의 프로퍼티 또는 메소드를 상속받아 사용할 수 있게 합니다. 이러한 부모 객체를 프로토타입 객체 또는 프로토타입이라고 합니다.
프로토타입 객체는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용합니다.
- [[Prototype]]
- 함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯입니다.
- 객체의 입장에서는 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 Function.prototype를 가리킨다.
- prototype 프로퍼티
- 함수 객체만 가지고 있는 프로퍼티입니다.
- 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체를 가리킵니다.
Prototype chain
자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색합니다. 이를 프로토타입 체인이라고 합니다.
var person = {
name: 'Lee',
gender: 'male',
sayHello: function(){
console.log('Hi! my name is ' + this.name);
}
};
console.dir(person);
console.log(person.__proto__ === Object.prototype); // ① true
console.log(Object.prototype.constructor === Object); // ② true
console.log(Object.__proto__ === Function.prototype); // ③ true
console.log(Function.prototype.__proto__ === Object.prototype); // ④ true
확장
프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있습니다. 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 객체이 반영됩니다.
function Person(name) {
this.name = name;
}
var foo = new Person('Lee');
Person.prototype.sayHello = function(){
console.log('Hi! my name is ' + this.name);
};
foo.sayHello();
실행 컨텍스트(Execution context)
쉽게 말하면 코드들이 실행되기 위한 환경이라고 이해하면 된다고 합니다. 코드가 실행되면 이 실행 컨텍스트 내부에서 실행되고 있는 것입니다.
자바스크립트 엔진은 코드를 실행하기 위해 아래와 같은 실행에 필요한 정보들을 알고 있어야 합니다.
- 변수 : 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
- 함수 선언
- 변수의 유효범위(scope)
- this
이와 같이 실행에 필요한 정보를 형상화하고 구분하기 위해 자바스크립트 엔진은 실행 컨텍스트를 물리적 객체의 형태로 관리합니다.
var x = 'xxx';
function foo () {
var y = 'yyy';
function bar () {
var z = 'zzz';
console.log(x + y + z);
}
bar();
}
foo();
위 코드를 실행하면 아래와 같이 실행 컨텍스트 스택이 생성하고 소멸합니다.
실행 컨텍스트의 3가지 객체
실행 컨텍스트는 실행 가능한 코드를 형상화하고 구분하는 추상적인 개념이지만 물리적으로는 객체의 형태를 가지며 3가지 프로퍼티를 소유합니다.
Variable Object(VO/변수객체)
실행 컨텍스트가 생성되면 엔진은 실행에 필요한 여러 정보들을 담을 객체를 생성합니다. 이를 VO라고 합니다. VO는 코드가 실행될 때 엔진에 의해 참조되며 코드에서는 접근할 수 없습니다.
VO는 아래의 정보를 담고 있습니다.
- 변수
- 매개변수와 인수 정보
- 함수 선언
VO는 실행 컨텍스트의 프로퍼티이기 때문에 값을 갖는데 이 값은 다른 객체를 가리킵니다. 전역 코드 실행시 생성되는 전역 컨텍스트와 함수를 실행할 때 생성되는 함수 컨텍스트의 경우 가리키는 객체가 다릅니다. 그 이유는 전역 코드와 함수의 내용이 다르기 때문입니다.
VO가 가리키는 객체는 아래와 같습니다.
전역일 경우
VO는 유일하며 최상위 위치하고 모든 전역 변수, 함수 등을 포함하는 전역 객체(GO)를 가리킵니다. 전역 객체는 전역에 선언된 전역 변수와 전역 함수를 프로퍼티로 소유합니다.
함수일 경우
VO는 활성객체(AO)를 가리키며 매개변수와 인수들의 정보를 배열의 형태로 담고 있는 arguments object가 추가됩니다.
Scope Chain
스코프 체인은 일종의 리스트라고 생각하면 됩니다. 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례대로 저장하고 있습니다. 다시 말하자면 해당 전역 또는 함수가 참조할 수 있는 변수, 함수 선언 등의 정보를 담고 있는 GO 또는 AO 리스트를 가리킵니다.
스코프 체인은 식별자 중에서 객체(전역 객체 제외)의 프로퍼티가 아닌 식별자, 즉 변수를 검색하는 메커니즘이다.
식별자 중에서 변수가 아닌 객체의 프로퍼티(물론 메소드도 포함된다)를 검색하는 메커니즘은 프로토타입 체인(Prototype Chain)이다.
this
this 프로퍼티에는 this 값이 할당됩니다. this에 할당되는 값은 함수 호출 패턴에 의해 결정됩니다.
클로저(closure)
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
return innerFunc;
}
/**
* 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
* 그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
*/
var inner = outerFunc();
inner(); // 10
자신을 포함하는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수를 호출되더라도 지역 변수에 접근할 수 있는데 이러한 함수를 콜로저라고 부릅니다.
위의 정의를 다시 살펴보면 함수
란 반환된 내부함수를 그 함수가 선언될 떄의 렉시컬 환경이란 내부 함수가 선언됐을 때의 스코프를 의미합니다. 즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경인 스코프를 기억하여 환경 밖에서 호출되어도 그 환경에 접근할 수 있는 함수를 말합니다. 더 쉽게 말하자면 자신이 생성될 때의 환경을 기억하는 함수라고 기억하면 될 것 같습니다.
클로저에 의해 참조되는 외부함수의 변수를 자유 변수라고 부릅니다. 클로저라는 이름은 자유변수에 엮여있는 함수라는 뜻입니다.
위의 그림을 보면 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도 활성 객체는 내부함수에 의해 참조되는 한 유효하여 스코프 체인을 통해 참조할 수 있습니다. 외부함수가 반환되었어도 외부함수 내의 변수는 이를 필요로 하는 내부함수가 하나 이상 존재하는 경우 계속 유지됩니다.
'프론트엔드 스터디 > Javascript' 카테고리의 다른 글
자바스크립트의 이벤트 (0) | 2021.07.14 |
---|---|
자바스크립트에서 객체지향 프로그래밍이란? (0) | 2021.07.14 |
클로저(closure)란??? (0) | 2021.07.13 |
자바스크립트 실행 컨텍스트 (0) | 2021.07.12 |
자바스크립트에서 scope란? (0) | 2021.07.12 |