Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발자공부일기

자바스크립트 기초 문법4 본문

TIL(Today I Learned)

자바스크립트 기초 문법4

JavaCPP 2024. 11. 6. 21:01

스코프 체인(Scope Chain)

 

스코프 체인(Scope Chain)은 자바스크립트에서 변수와 함수의 접근 권한을 결정하는 중요한 개념입니다. 스코프 체인은 함수가 실행될 때, 변수와 함수가 검색될 수 있는 범위를 정의하는 규칙입니다. 자바스크립트는 렉시컬 스코핑(lexical scoping)을 따르기 때문에, 변수나 함수가 어디에 선언되었는지에 따라 접근 가능한 범위가 결정됩니다.

1. 스코프(Scope)란 무엇인가?

스코프는 코드에서 변수와 함수유효한 범위를 의미합니다. 즉, 코드 내에서 특정 변수나 함수가 어디에서 접근 가능한지를 결정합니다. 스코프는 크게 전역 스코프(Global Scope)함수 스코프(Function Scope)로 나눌 수 있습니다.

  • 전역 스코프: 프로그램의 모든 코드에서 접근 가능한 범위. 함수 외부에서 정의된 변수나 함수는 전역 스코프에 속합니다.
  • 함수 스코프: 함수 내에서 정의된 변수나 함수는 그 함수 내에서만 접근 가능합니다. 함수가 호출될 때마다 새로운 함수 스코프가 생성됩니다.

2. 렉시컬 스코핑(Lexical Scoping)

자바스크립트는 렉시컬 스코핑을 따릅니다. 이는 변수나 함수가 어디에서 정의되었는지에 따라 스코프가 결정된다는 의미입니다. 예를 들어, 함수가 정의된 위치에 따라, 해당 함수 내에서 어떤 변수에 접근할 수 있는지가 결정됩니다.

3. 스코프 체인(Scope Chain)의 동작

스코프 체인은 변수를 찾기 위해 스코프를 거슬러 올라가는 방식입니다. 자바스크립트 엔진은 현재 실행 중인 함수의 스코프에서 변수나 함수를 찾고, 만약 찾을 수 없다면 상위 스코프로 올라가서 계속해서 찾습니다. 이 과정을 통해 변수나 함수가 선언된 위치를 기준으로 중첩된 스코프를 따라가며 검색됩니다.

스코프 체인의 예시

var globalVar = 'I am global';

function outer() {
  var outerVar = 'I am outer';
  
  function inner() {
    var innerVar = 'I am inner';
    console.log(innerVar);  // (1)
    console.log(outerVar);  // (2)
    console.log(globalVar); // (3)
  }
  
  inner();
}

outer();

실행 순서와 스코프 체인:

  1. outer 함수가 호출됩니다. outer 함수는 전역 스코프(global scope)에서 호출됩니다.
  2. outer 함수의 스코프 안에서 outerVar가 정의됩니다.
  3. outer 안에서 inner 함수가 호출되며, inner 함수가 실행됩니다.
  4. inner 함수는 자신만의 스코프를 갖고 있으며, 그 안에서 innerVar가 정의됩니다.
  5. inner 함수에서 첫 번째 console.log(innerVar)는 innerVar를 자기 스코프에서 찾고 출력합니다.
  6. 두 번째 console.log(outerVar)는 outer 스코프에서 outerVar를 찾고 출력합니다.
  7. 세 번째 console.log(globalVar)는 전역 스코프에서 globalVar를 찾고 출력합니다.

출력:

I am inner
I am outer
I am global

4. 스코프 체인의 내부 구조

자바스크립트에서 함수가 실행되면, 실행 컨텍스트(Execution Context)가 생성되고 그 안에서 스코프 체인이 형성됩니다. 스코프 체인은 다음과 같은 순서로 구성됩니다:

  1. 현재 함수의 스코프: 가장 먼저 현재 실행 중인 함수의 스코프가 찾아집니다.
  2. 상위 함수의 스코프: 만약 현재 함수에서 찾을 수 없는 변수나 함수가 있으면, 그 상위 함수의 스코프를 찾아갑니다.
  3. 전역 스코프: 최종적으로, 함수 외부에서 정의된 변수나 함수가 필요한 경우 전역 스코프에서 변수나 함수가 검색됩니다.

이렇게 스코프 체인은 현재 실행 중인 함수에서부터 전역 스코프까지 거슬러 올라갑니다.

5. 예시: 클로저(Closure)와 스코프 체인 

아직 안배웠지만 일단 Chat GPT가 알려주길래 살펴봤다.

클로저는 함수가 선언될 당시의 스코프 체인을 기억하는 특성을 가진 함수입니다. 클로저는 자신이 선언될 때의 스코프를 기억하고, 그 스코프 내의 변수들에 접근할 수 있습니다.

function outer() {
  var outerVar = 'I am outer';

  return function inner() {
    console.log(outerVar); // inner 함수에서 outer 함수의 변수에 접근
  };
}

var closure = outer();
closure(); // I am outer

동작 원리:

  • outer 함수가 실행될 때, inner 함수가 반환됩니다.
  • inner 함수는 outer 함수의 스코프기억하고 있기 때문에, 나중에 closure()를 호출할 때에도 **outerVar**에 접근할 수 있습니다.

이처럼 클로저는 스코프 체인을 기억하고, 그 안에서 변수에 접근할 수 있는 능력을 갖고 있습니다.

6. this와 스코프 체인

this는 함수 호출 방식에 따라 다르게 바인딩됩니다. 스코프 체인과는 다르게 this는 호출된 컨텍스트에 따라 결정됩니다. 하지만, 화살표 함수에서는 this가 렉시컬하게 바인딩되므로, this는 상위 스코프의 this를 그대로 사용합니다.

결론

  • 스코프 체인은 함수가 실행될 때, 변수나 함수가 정의된 위치를 기준으로 상위 스코프부터 차례대로 변수나 함수를 검색하는 메커니즘입니다.
  • 자바스크립트는 렉시컬 스코핑을 사용하므로, 함수가 선언된 위치에 따라 어떤 변수나 함수에 접근할 수 있는지가 결정됩니다.
  • 스코프 체인은 함수의 스코프에서 상위 스코프를 거쳐 전역 스코프까지 거슬러 올라가며, 필요한 변수나 함수가 있을 경우 그 위치에서 찾게 됩니다.

 

call,apply,bind

 

1. call

call 메서드는 함수를 즉시 호출하면서 this로 사용할 객체와 함수의 인수를 개별적으로 전달할 수 있게 해 줍니다.

문법

functionName.call(thisArg, arg1, arg2, ...)
  • thisArg: 호출하는 함수 내부에서 this로 사용할 객체입니다.
  • arg1, arg2, ...: 함수에 전달할 인수들을 개별적으로 나열합니다.

예제

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Alice' };

greet.call(person, 'Hello', '!'); // Hello, Alice!

위 예제에서 greet 함수는 this.name을 사용하여 this가 가리키는 객체의 name 속성을 참조합니다. greet.call(person, 'Hello', '!')에서 call을 사용하여 thisArg를 person 객체로 설정하면 this.name은 person.name, 즉 'Alice'가 됩니다.

 

 


2. apply

apply 메서드는 call과 거의 동일하게 작동하지만, 함수의 인수를 배열이나 배열과 유사한 객체(예: arguments 객체)로 전달한다는 점에서 다릅니다.

문법

functionName.apply(thisArg, [argsArray])
  • thisArg: 함수 내부에서 this로 사용할 객체입니다.
  • argsArray: 함수에 전달할 인수들을 배열 형태로 전달합니다.

예제

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person = { name: 'Bob' };

greet.apply(person, ['Hi', '!']); // Hi, Bob!

apply를 사용하면 greet.apply(person, ['Hi', '!'])와 같이 인수를 배열(['Hi', '!'])로 묶어서 전달할 수 있습니다.

call과 apply의 차이점 요약

  • call: 인수를 개별적으로 나열하여 전달합니다.
  • apply: 인수를 배열 형태로 전달합니다.

예시: 최대값 구하기

const numbers = [1, 2, 3, 4, 5];
console.log(Math.max.apply(null, numbers)); // 5

여기서 apply를 사용해 Math.max에 numbers 배열을 전달해 최대값을 구했습니다.


3. bind

bind 메서드는 call이나 apply와 달리 즉시 함수를 호출하지 않고, this가 고정된 새로운 함수를 반환합니다. 이렇게 생성된 함수는 나중에 호출할 수 있으며, 원래 함수와 동일한 인수를 받습니다.

문법

const boundFunction = functionName.bind(thisArg, arg1, arg2, ...);
  • thisArg: 새 함수가 호출될 때 this로 사용할 객체입니다.
  • arg1, arg2, ...: 고정할 인수입니다. bind로 생성된 함수는 이 인수를 첫 번째 인수로 항상 받게 됩니다.

예제

const person = {
  name: 'Charlie',
  sayHi: function() {
    console.log('Hi, ' + this.name);
  }
};

const sayHiBound = person.sayHi.bind(person);
sayHiBound(); // Hi, Charlie

sayHiBound는 person.sayHi를 this가 person으로 고정된 상태로 저장한 새 함수입니다. sayHiBound()를 호출하면 this.name은 항상 person.name('Charlie')로 참조됩니다.

부분 적용 (Partial Application)

bind는 부분 적용(partial application)에도 유용합니다. bind 호출 시 고정된 인수를 전달하면, 새 함수가 호출될 때 해당 인수가 자동으로 포함됩니다.

function multiply(a, b) {
  return a * b;
}

const double = multiply.bind(null, 2); // a를 2로 고정
console.log(double(5)); // 10

여기서 double 함수는 multiply 함수의 첫 번째 인수로 항상 2를 받도록 고정되었습니다.


요약과 차이점

 

메서드 즉시 호출 여부 this 설정인수 전달 방식
call 즉시 호출 O 개별 인수 나열
apply 즉시 호출 O 배열 형태로 전달
bind 즉시 호출하지 않음 O 인수 고정 가능
  • call: this와 인수를 즉시 전달하여 함수를 호출합니다.
  • apply: this와 인수를 배열로 전달하여 함수를 호출합니다.
  • bind: this와 인수를 고정하여 새로운 함수를 반환하며, 나중에 호출할 수 있습니다.

 

call과 apply로 상속 구현하기

function Person(name, gender) {
	this.name = name;
	this.gender = gender;
}
function Student(name, gender, school) {
	Person.call(this, name, gender); // 여기서 this는 student 인스턴스
	this.school = school; 
function Employee(name, gender, company) {
	Person.apply(this, [name, gender]); // 여기서 this는 employee 인스턴스
	this.company = company;
}
var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');


console.log(kd); //Student { name: '길동', gender: 'male', school: '서울대' }
console.log(ks); //Employee { name: '길순', gender: 'female', company: '삼성' }

 

Student 생성자에서 person이란 생성자를 call하면서 name,gender에 인수로 받은 값을 넣는다.school도 마찬가지

6,7번째줄에서 this는 13번째줄에서 new로 생성되며 생긴 student를 자동으로 지목한다.

 

Employee생성자에서도 똑같이 동작한다. 다만 인수에 []로 감싸서 넣었다.