[TIL]scope/closer

Violet Lee·2020년 9월 2일
1

javascript

목록 보기
7/24

우선 TIL을 작성하기전에...

나는 왜 이전에 배운 scope와 closer부분을 TIL로 올릴까?
우선 다시 공부하여 개념을 확고히 해야겠다는 마음이 생긴 문제 중 하나가 함수와 클로저 이며, 그다음으로 비동기함수이고, 그 다음이 알고리즘의 원리, 그 다음이 객체등등 이다. 우선은 함수부터 차근차근 다시 개념 재정립을 하겠다.
일단 이전에 나는, 강의를 듣고 그걸 바로 문제에 응용을 하느라, 정확히는 문제를 푸는거에만 집중을 하느라 따로 배운 내용을 블로깅할 시간을 마련하지않았다. 그래서 기수에 탑승시 혹시라도 기본적인 개념을 몇몇개 잊어먹은채로 가는 불상사를 모면하기위해, 내 자신이 나태해지지 않기위해, 계속해서 공부하여 까먹지 않기위해 다시 공부하여 정리해본다.

scope

: 변수 접근규칙에 따른 유효범위. 전역범위, 지역범위 등이 있음.

ex)//greetSomeone()과 firstName 실행의 결과는?

let greeting = 'Hello'; // global scope(전역함수)(최상단의 scope. 전역쪽에서 지역함수/변수를 접근하는건 불가능)
			//(전역변수는 접근이 쉽다.)
function greetSomeone(){  //local scope(지역함수)
    let firstName = 'Josh';
    return greeting + ' ' + firstName;// local scope(지역함수)(지역에서 전역함수/변수를 접근하는것은 가능)
} 			              //(전역보다 지역변수 우선순위 높음)
greetSomeone(); // 'Hello Josh'. 
firstName;      // global scope(전역함수). 
// -> Reference error: firstName is not defined; (지역함수의 범위라서 참조 불가능하다.)
ex)//순서대로 콘솔에 출력되는 결과는?

let name = 'Richard';
function showName(){
   name = 'Jack'; //선언이 없어, 바깥 scope의 name을 갖고와서 재선언한다.
   console.log(name); //Jack
}
console.log(name); // showName()이 아직 실행안됐으니까 전역변수 Richard.
showName(); // 실행됐으므로 Jack.
console.log(name); // showName()의 실행으로인해 재할당이 일어났으므로 Jack. 

block scope / function scope

  • block : 중괄호로 시작하고 끝나는 단위

잠깐. 짚고 넘어가기.
이전에 페어분과 twittler과제를 진행했을때의 일이다.
우리 모두 기능을 구현하느라 지치고 힘든상황에서, 팀활동에 어찌보면 가장 중요한, 변수명 공통선언, 공통 키워드 선언, 그리고 어떤 함수를 먼저 써서 가독성을 편리하게 하느냐.. 를 점차 잊어가고 아이디어가 나오는대로 우리의 손은 움직이고 있었다. 그 문제들중 하나가 var였다. 지금은 짧은 프로젝트이기때문에 var를 마음껏써도 우리가 어떤 변수명을 이미 썼는지 알기때문에 상관이 없었다. 그러나 정말로 SNS서비스 하나를 전부 구현해야한다면? 점차 중복되는 클래스이름이나 id가 우리도 모르게 나올거고, 에러는 정말로 찾기힘들거다. 웬만하면 let을 사용하는 방법을 익혀야한다고 생각한다.

  • var : 함수단위로 자신만의 scope가짐
  • let: block단위로 scope 구분했을때에 예측이 더 쉬움.
ex)//let과 var의 경우, 콘솔에 출력되는 결과는?
for(var i=0;i<5;i++){ //for(let i=0;i<5;i++){
  console.log(i);  
}
console.log('final i: ',i); //0,1,2,3,4, var - final: 5/ let - reference error
// let: block범위를 벗어나면 당연히 i 사용이 불가하다. 
// var: i가 호이스팅이 되기때문에, '전역변수'가 되서, 출력이 잘되는것이다. 
//      block범위를 벗어나도, 같은 function scope에서는 사용이 가능하다.
//      그렇지만 그렇게되면 반대로 호이스팅이 되서 레퍼런스에러가 뜨는 불상사가 발생하거나, 중복선언을 해도 전혀 문제가 없게되는,
//      우리가 흔히아는 문제들이 벌어진다.

↑ 그렇다면 var의 경우,use strict를 사용하면 되지 않을까.?

 function a(){
	'use strict'; //strict mode적용해줌. 문법적으로 실수할수있는 부분들을 에러로 판단한다.
	for(var i=0;i<5;i++){ //for(let i=0;i<5;i++){
		console.log(i);  
	}
}
console.log('final i: ',i);
//> Uncaught ReferenceError: i is not defined
// 흠. 그렇지만 뭔가 변수 하나때문에 너무 많은일을 한다고 생각된다.. 그러므로
//재선언이 처음부터 불가능한 let,const등을 사용하는게 편리하다.
  • const : immutable(재할당불가)한 키워드이다.
    먼저 변수선언을 한 이후에 값을 할당해주는것-> 불가능.
    물론 값을 재할당하는것-> 불가능.
    => 즉, 처음에 변수를 선언함과 동시에 값을 할당해줘야 한단 소리다.
ex) const를 사용해 할당하는 경우
const arr = []; //빈 배열 선언
const toBePushed = 42; // const 는 참조형변수(object,array,function())를 사용하기에 바람직하다 한다. 
    //왜냐면, 이 경우에 어떤 값을 할당해도, const변수 자체가 재할당된것이 아닌, 안의 프로퍼티만 추가,삭제되므로 가능한것이다.
    //그러니까 const는 참조하는 메모리주소가 변하지 '않는'변수를 선언할시 사용하는것이다.
    arr.push(toBePushed); //push() : push()후 총 길이 반환. 

    // 답: expect(arr).toEqual([42]); 

  • window 객체: 콘솔에 'window.'을 치고 엔터누르면 윈도우객체의 속성,메소드들이 엄청나게 뜬다.
    즉, 모든 객체의 조상이므로 전역객체이다. 즉, 생략이 가능하므로 우리는 모든 속성,메소드들을 사용할때 생략이 가능했던거다.
ex) 
var myName = 'Paul';
console.log(window.myName); //Paul

function foo(){
	console.log('bar');
}
console.log(foo === window.foo); //true

closer

: 일급함수를 변수에 대입하고, 반환값으로 반환하는 작업이 필요함.
클로저는 자신을 포함하고있는 외부함수의 인자, 지역변수등을 외부함수가 종료된후에도, 사용이 가능하다.
이러한 변수를 자유변수(free variable)이라고 하며, 클로저는 즉, 자유변수를 가지는 코드이다.

ex) 기본 scope
function main(){
	var name = 'name';
}
ex)//차례대로 어떤 함수가 찍힐까.
function outerFn(){
    let outerVar = 'outer';
    console.log(outerVar);

    function innerFn(){  //클로저 함수. 이 안에서는 지역변수(innerVar),외부함수의 변수(outerVar),전역변수(globalVar)의 접근이 전부가능.
        let innerVar = 'inner';
        console.log(innerVar);
    }
    return innerFn; //innerVar
}
let globalVar = 'global';
outerFn(); // 콘솔로그로 outer가 먼저 찍히고, innerFn() 함수 자체가 실행됨.
outerFn()(); //처음 괄호는 innerFn()함수가 실행되어 outer가 먼저찍히고, 두번째괄호는 그안의 console.log값이 찍혀서 outer 그리고 inner가 출력됨. 이게 js에선 가능.
ex)//innerFn 함수에서 접근할수있는 scope는 총 몇개?

function outerFn(){
    let outerVar = 'outer';
    console.log(outerVar);

    function innerFn(){
        let innerVar = 'inner';
        console.log(innerVar);
    }
}
let globalVar = 'global';
outerFn();

//맨 마지막은 전역 스코프, outer와 inner는 각자의 스코프를 가짐.
-> 그러므로 3.

커링:

함수 하나가 n개의 인자를 받는대신, n개의 함수를 만들어서 각각 인자를 받게하는 방법.

ex)
function adder(x){
    return function(y){
        return x+y;
    }
}


//순서 (1)
adder(2)(3); //답: 5. x와 y를 고정으로 넣어놓고 값을 호출.

//순서 (2)
let add100 = adder(100); //이 경우 x의 값을 고정해놓고,재사용할수 있다.
                         //즉 x에 항상 100을 넣어놓을수 있다.

add100(2); //답: 102. 안의 클로저함수에 y값인 2를 넣어서 계산한 값이 도출되었다.
add100(10); //답: 10. 위와 동일하다.

let add5 = adder(5); //'클로저는 x값 재할당이 가능'하다.
add5(2); //7. 그러므로 클로저함수에 y값 2를 넣으면 7이 나온다.


클로저 모듈 패턴

: 변수를 스코프 안으로 가두어 함수밖으로 노출시키지않는 방법.

function makeCounter(){
	let counter=0;
	
	let obj={ ===(= return)
		incre : function(){
			counter++;
		
		},
		decre : function(){
			counter--;
		},
		value : function(){
			return counter;
		}
	
	}


}

let count1 = makeCounter();
count1.incre();
count1.incre();
count1.vlaue(); //2

let count2 = makeCounter();
count2.decre(); //1. 함수 값을 호출할것이므로 decre()로 써야함. 만약 decre에 할당된 값이 함수가 아니었다면 ()붙일필요 x.
count2.decre(); //0
count2.incre(); //1
count1.value(); //1

=> 두 카운터에 각기 다른 카운터를 다루면서, 카운터를 밖으로 노출시키지 않는다. 각각 별개의 함수를 사용한다.
  또한 그 카운터를 다루는것은 서로의 변수에 영향을 안 끼치는걸 볼수있다.
ex) [클로저 모듈 패턴이 어떨때 유용하게 쓰이는지 알수있는 예제] 일케 원하는 클로저함수만 사용할수 있다. 

function makePayment(){
    let type = '현금'; //반드시 현금 또는 카드

    return{
        payWithCash : function(amont){
            type = '현금';
            console.log(type + '으로'+amount+"만큼 지불합니다.");
        },
        payWithCard : function(amount){
            type = '카드';
            console.log(type + '으로'+amount+"만큼 지불합니다.");
        }
    }
}

ex) multiplyByX , multiplyByFive 중 '클로저사용'을 보여주는 함수는?

  var multiplyByX = function(x) { //안에 x*y를 해줄수있는 익명의 클로저함수가 담긴 함수를 변수에 할당함. 
  return function(y) {
    return x * y;
  }
}
var multiplyBy5 = multiplyByX(5); //위의 함수의 x값에 5를 넣어주겠음. 외부함수의 인자에 5가 잘 들어감. 
//현재 multiflyBy5 변수에는 y를 넣어주면 언제든지 계산해서 리턴가능한 익명의 클로저함수가 담겨있는 상태.
multiplyBy5(4); //이제 여기서 4를 y에 넣어줌. 현재 값은 20.

var multiplyByFive = function() { //인자가 없는 함수안에는, 5*y를 해줄수있는 클로저함수가 담겨있음	
  return function(y) { 
    return 5 * y;
  }
}

var multiplyBy5 = multiplyByFive(); //함수를 호출시 인자를 넣어주지않음. 외부함수가 인자를 쓰지않기때문.
//내가 실험을 하나했음. 그렇다면 만약 multiplyBy5 변수에 호출시 인자를 넣어버린다면, 안에 클로저함수의 y값에 들어갈까..? 
//>> 결과는 no였다. 무조건 해당함수가 외부함수건 클로저함수건 '인자를 받을수있는 상태'가 아니라면 들어갈수없다. 
//그냥 그저, 안의 클로저함수가 출력이 되었다.. ; 

//> 그러므로 현재 multiplyBy5 안에는 multiplyByFive() 자체가 '재할당'되어 들어간 상태.
//여기서 multiplyBy5(4); 를 해준다면 결과는 아까와 같이 20이 나온다. 

>>:, 클로저는 '외부함수의 컨텍스트에 접근할 수 있는 내부함수' 여야 하므로 multiplyByX이다.

profile
예비개발자

0개의 댓글