본문 바로가기

[JS] test를 통한 scope 개념 익히기 _Koans

[JS] test를 통한 scope 개념 익히기 _Koans

Koans는 불교에서 유래된 단어로, 결론을 내리기 전에 이게 왜 맞는지 깊게 고민한다는 의미를 가지고 있다고 한다. 답이 미리 제시되어 있기 때문에 고민 없이 풀면, 큰 어려움 없이 전부 다 풀 수 있지만 그게 왜 정답인지 깊게 고민해 보는 시간을 갖지 않는다면 충분히 성장하기 어려울 것이다. 즉, '왜?' 라는 물음에 대해 꼭 깊은 고찰을 하고 넘어가자는 의미이다.


 

describe('scope 대해서 학습합니다.', function () {
// scope는 변수의 값(변수에 담긴 값)을 찾을 때 확인하는 곳을 말한다. (반드시 기억!!!) 

it('함수 선언식(declaration)과 함수 표현식(expression)의 차이를 확인합니다.', function () {
let funcExpressed = 'to be a function';

expect(typeof funcDeclared).to.equal('function'); 

// funDeclared 함수는 아래에 있는데 벌써 typeof의 값이 'function'으로 나온다.
// 자바스크립트 함수 호이스팅(hoisting)에 대한 개념이다.

expect(typeof funcExpressed).to.equal('string');

function funcDeclared() {
return 'this is a function declaration';
}

funcExpressed = function () {
return 'this is a function expression';
};

const funcContainer = { func: funcExpressed };
expect(funcContainer.func()).to.equal('this is a function expression');

funcContainer.func = funcDeclared;
expect(funcContainer.func()).to.equal('this is a function declaration');
});

함수 호이스팅

  • 호이스팅: 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다. (해당 스코프의 최상단)
  • funcDeclared 함수가 아래에 있기 때문에 함수 위의 typeof가 참조를 못해야 하는데 값이 'function'이 나온다.
    함수 선언문으로 정의된 함수는 호이스팅이 일어난 것처럼 보인다. 미리 실행도 가능하다.
    (함수 선언문은 해당 함수를 포함하는 블록의 최상단으로 옮겨진다.)
  • funcExpressed 문자열에서 함수로 재할당을 했는데 중간 위치에서 타입을 조회했을 때 스트링이 나온다. 
    함수 표현식으로 작성된 함수는 호이스팅이 일어나지 않는다. 그래서 표현식 사용을 권장!

호이스팅이 일어나지 않도록 하기 위해서는 함수 표현식을 쓰자!!!!!!!

  • 참고!
    • var로 선언된 변수의 경우 undefined로 초기화가 된다.
    • let const로 선언된 변수는 호이스팅이 발생하지 않는 것처럼 동작한다. (선언되었다는 정도만 메모리에 저장)
    • 변수의 할당문은 호이스팅이 되지 않는다. (함수 표현식과 같음)

it('lexical scope에 대해서 확인합니다.', function () {
let message = 'Outer';

function getMessage() {
return message;
}

function shadowGlobal() { 
let message = 'Inner';
return message;
}

function shadowGlobal2(message) {
return message;
}

function shadowParameter(message) {
message = 'Do not use parameters like this!';
return message;
}

expect(getMessage()).to.equal('Outer');
expect(shadowGlobal()).to.equal('Inner');
expect(shadowGlobal2('Parameter')).to.equal('Parameter');
expect(shadowParameter('Parameter')).to.equal('Do not use parameters like this!'); // **
expect(message).to.equal('Outer'); //
  • 1) 함수 내부에 message 선언과 할당이 없다. (외부에서 참조)
  • 2) 함수 내부에 message 선언과 할당이 있다.  (내부에서 참조) 
  • 3) 함수 내부에 메시지 변수가 있다고 봐야 한다. (매개변수가 있기 때문에) 매개변수를 참조한다.
  • 4) 함수 내부에서 재할당을 하고 있다.
    let message = 'Inner';이었다면 거기에 재할당이 되겠지만, 없기 때문에 외부 변수에 재할당이 된다. 
  • message = 'Parameter' 할당이 됬다. 그 이후 재할당을 해주고 있기 때문에 재할당이 된다. 

중간 사례 진짜 머리가 어질하다;



주기적으로... 들여다보자...

 


 

it('default parameter에 대해 확인합니다.', function () {
function defaultParameter(num = 5) {
return num;
}

expect(defaultParameter()).to.equal(5);         // 이렇게도 구현되는구나**
expect(defaultParameter(10)).to.equal(10);      // 이렇게도 구현되는구나**

function pushNum(num, arr = []) {
arr.push(num);
return arr;
}

expect(pushNum(10)).to.deep.equal([10]);        // 이렇게도 구현되는구나**
expect(pushNum(20)).to.deep.equal([20]);
expect(pushNum(4, [1, 2, 3])).to.deep.equal([1,2,3,4]);
});

it('클로저(closure)에 대해 확인합니다.', function () {
function increaseBy(increaseByAmount) {          // 이렇게도 구현되는구나** 
return function (numberToIncrease) {             // 바로 inner 함수를 return (함수이름 없어도 됨)
return numberToIncrease + increaseByAmount;
};
}

const increaseBy3 = increaseBy(3);
const increaseBy5 = increaseBy(5);

expect(increaseBy3(10)).to.equal(13);
expect(increaseBy5(10)).to.equal(15);
expect(increaseBy(8)(6) + increaseBy(5)(9)).to.equal(28);
});

 

 

클로저

클로저는 함수와 함수가 선언된 어휘적 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회한다. 즉, 클로저는 내부(inner) 함수가 외부(outer) 함수의 지역 변수에 접근할 수 있다.

 

클로저의 특징: (1) 함수가 함수를 리턴하고 있네? (2)리턴되고 있는 함수(내부함수)가 외부 함수의 변수를 사용하고 있네? 

 


 

 

it('lexical scope와 closure에 대해 다시 확인합니다.', function () {
let age = 27;     
let name = 'jin';
let height = 179;    

function outerFn() {                                // 꼭 기억하기 **
let age = 24;
name = 'jimin';
let height = 178;

function innerFn() {
age = 26;
let name = 'suga';
return height;
}

innerFn();

expect(age).to.equal(26);
expect(name).to.equal('jimin'); 

return innerFn;
}

const innerFn = outerFn();

expect(age).to.equal(27);
expect(name).to.equal('jimin');
expect(innerFn()).to.equal(178);  
});
});

클로저가 잘 구현된 예이므로 꼭! 기억하기 

728x90
⬆︎