본문 바로가기
Development

[20.12.27] YDKJSY - Digging to the Roots of JS

by igy95 2024. 1. 7.

Iteration

  • for..of 구문
var object = /* .. */;

for (const value of object) {
    console.log(value);
}
//value 1..
//value 2..
  • spread operator
const arr = [1,2,3,4];
const arrCopy = [...arr];

console.log(arrCopy); // [1,2,3,4]
console.log(arr === arrCopy); //false

Iterables

Iterable : 해당 값 안에 있는 원소들을 반복 순회 시킬 수 있는 값.
ES6 에서 명시한 iterable 한 자료 구조 : String, Array, Map, Set etc.

  • spread operator 를 통해 문자열을 스플릿 할 수 있다.
var greeting = "Hello world!";
var characters = [...greeting];

console.log(characters);
// [ "H", "e", "l", "l", "o", " ",
//   "w", "o", "r", "l", "d", "!" ]
  • for..of 를 이용해 배열의 idxvalue 가져오기.
var arr = [10, 20, 30];

for (const [idx, value] of arr.entries()) {
    console.log(idx, value);
}
// 0 10
// 1 20
// 2 30
  • 대부분 iterable 한 자료 구조는 3 개의 iterator 형식을 가지고 있다.
  • keys(), values(), entries()
  • 각각 자료 구조의 키, 값, 키-값 쌍을 얻을 수 있는 iterator 이다.

Closure

클로저란, 함수가 다른 스코프에서 실행 되어도 선언 당시의 외부 스코프를 기억해서 해당 변수에 접근하는 매커니즘을 일컫는다.

 

주로 함수 안에서 함수를 가지고 있는 패턴에서 흔히 발견할 수 있다. 스코프의 여러 특징 중 lexical scope 에서 파생된 매커니즘인데, 다시 말해 선언한 이중 함수를 원형으로 두고 여러 개의 인스턴스를 생성해도 해당 인스턴스 스코프 안에서 함수가 선언 되었을 당시의 스코프를 기억해두고 있다가 외부의 변수 참조가 가능하다는 얘기다.

function counter(step = 1) {
    var count = 0;
    return function increaseCount(){
        count = count + step;
        return count;
    };
}

var incBy1 = counter(1);
var incBy3 = counter(3);

incBy1();       // 1
incBy1();       // 2

incBy3();       // 3
incBy3();       // 6

각각의 인스턴스는 선언 부의 외부 스코프인 counter 안의 두 변수 stepcounter 를 참조한다. 참조한 값을 계속 가지고 있기 때문에 값이 누적될 수 있는 것이다. 이러한 매커니즘은 콜백 함수를 이용한 비동기 방식에서 흔히 쓰인다. 외부 함수를 호출한 뒤에, 함수 내부의 콜백을 비동기적으로 호출했을 때 바깥 함수의 역할은 이미 완료되었지만 내부 함수는 외부 함수의 변수를 그대로 참조 한다.

하지만 꼭 외부 스코프가 함수일 필요는 없다. 이 매커니즘은 반복문 안에서도 적용이 가능하다.

for (let [idx,btn] of buttons.entries()) {
    btn.addEventListener("click",function onClick(){
       console.log(`Clicked on button (${ idx })!`);
    });
}

for 문 안의 idxbtnlet 키워드로 선언했기 때문에 각각의 변수들은 호출된 iteration 영역을 scope 로 가지고 있다. 그래서 addEventListener 함수는 자신들이 호출되기 전까지 해당 변수들을 기억하고 있기 때문에 각 버튼에 알맞는 핸들러를 등록할 수 있다.

This

this; // window {}

일반적으로 thiswindow 이다.

 

this 에 대한 오해들

  1. 함수 내부의 this 는 함수 자체를 가리키는 것이다.
  2. this 는 메소드가 속해 있는 인스턴스를 가리키는 것이다.

함수엔 scope rule 외에도 또 다른 특성이 있는데, this 키워드와 같이 언급되는 execution context 가 있다. scope 는 정적이고 고정된 변수들을 가지고 있지만, execution conext 는 동적이면서 전적으로 자신이 어떻게 호출되었는지에 따라 주체가 바뀐다. 함수 내부에 선언된 this 의 주체를 설정해주는 방법은 여러가지가 있다.

 

1. 외부에서 메소드나 콜백으로 호출하기

주로 함수 안의 함수, 클래스의 메소드 혹은 이벤트 리스너 등 다양한 방법으로 this 를 포함한 함수의 execution context 를 잡아줄 수 있다.

 

2. bind, call, apply

function classroom(teacher) {
    return function study() {
        console.log(
            `${ teacher } says to study ${ this.topic }`
        );
    };
}

var assignment = classroom("Kyle");
assignment();
// Kyle says to study undefined  -- Oops :(

var otherHomework = {
    topic: "Math"
};

var binded = assignment.bind(otherHomework);
binded();
assignment.call(otherHomework);
assignment.apply(otherHomework);
// Kyle says to study Math

함수를 선언해준 후에 위의 세가지 api를 이용해 this 가 참조할 object를 호출하는 방법이 있다. 해당 api 마다 사용 방법이 따로 있긴 하지만 그 부분은 나중에 다뤄봐야겠다.

 

3. 생성자 함수

모듈 내부에서 생성자 함수를 선언하면 그 안에 초기화를 위해 this 를 사용한다. 만약 선언한 함수를 그대로 호출한다면 해당 this 의 주체는 전역이 되버리고 결국 window 에서 초기화가 되고 만다. 이와 같은 오류를 방지하기 위해서는, new 키워드를 통해 인스턴스를 생성해주고 생성자 함수의 this 가 인스턴스를 참조하도록 해주면 된다. (ES6 부터는 이러한 문제를 방지하기 위해서 클래스 개념이 도입되었다.)