출처: https://bumcrush.tistory.com/182 [맑음때때로 여름]

상세 컨텐츠

본문 제목

Week3(Javascript)

MOFE 스터디

by 장동균 2020. 10. 2. 16:55

본문

1. null과 undefined

 

null과 undefined는 모두 자바스크립트에서 값이 없음을 나타낸다.

let foo; // foo의 type은 undefined가 된다.
let foo=null; // foo의 type은 null이 된다. null은 이렇게 명시적으로 값을 넣어주어야 한다.

그렇다면 이 둘의 차이는 무엇일까?

바로 등록과 저장 여부의 차이이다.

null은 값은 값이지만 값으로써 의미없는 특별한 값이 등록되어 있는 것.
undefined는 등록이 되어있지 않기 때문에 초기화도 정의되지도 않는 것.

이런 차이에도 불구하고 null과 undefined는 모두 값이 없음을 의미하고 서로 바꿔 사용하는 경우도 많이 존재한다.

이 둘은 동등연산자(==)를 사용하면 같다고 나오기에, 둘을 구별하기 위해 엄격한 일치연산자(===)를 사용하는 것이 좋다.

 

webclub.tistory.com/1

 

undefined와 null의 차이점

undefined vs null 이 두 타입은 모두 자바스크립트에서 '값이 없음'을 나타냅니다. 기본적으로 값이 할당되지 않은 변수는 undefined 타입이며,  undefined 타입은 변수 자체의 값 또한 undefined 입니다. ��

webclub.tistory.com


2. 간략 계산법 (Short-circuit Evaluation)

 

chanspark.github.io/2017/11/28/ES6-%EA%BF%80%ED%8C%81.html?fbclid=IwAR2zlpT6YiVU5Q9ZYCEygKgCcBgjcgQZFmAmfMMj45YnnT6LmNy_QSmYDhw

 

[번역]ES6 축약코딩 기법 19가지

자바스크립트를 스마트하게 사용해보자

chanspark.github.io

이곳에 나와 있는 모든 예제들이 다 중요하다고 생각하지만 내가 잘 알지 못했던 2번과 6번의 간략 계산법에 대해 설명한다.

 let variable1;
 let variable2 = variable1  || '';
 console.log(variable2 === ''); // prints true

 variable1 = 'foo';
 variable2 = variable1  || '';
 console.log(variable2); // prints foo

이 깃헙 블로그에 있는 예제(6번)를 그대로 가져왔다.

2행을 보자. variable1의 값이 undefined이기 때문에 or 연산자 뒤의 것이 variable2 속에 들어갔다.

 

6행을 보게되면 variable1의 값이 'foo' 이기에 이 값이 그대로 variable2 속에 들어갔다.

 

즉, 대입연산자(=) 이후에 나오는 변수들의 맨 왼쪽에서부터, 각 변수가 3개(undefined, null, '')의 경우 중 한개일 경우 그 오른쪽으로 넘어가고 그렇지 않다면 그 값을 대입하는 식이다. 맨 마지막까지도 3개의 경우 중 한개라면 그 값을 넣게 된다.

 

몇가지 경우를 예로 들자면

 let variable1;
 let variable2;
 let variable3 = variable1 || variable2 || "";

 console.log(variable3 === ""); //true

① variable1이 3가지 경우들 중 한 개인 undefined이기 때문에 그 오른쪽 variable2로 넘어간다.

② variable2 또한 3가지의 경우들 중 한 개인 undefined이기 때문에 그 오른쪽으로 넘어간다.

③ 3가지 경우들 중 한 개인 ""이기는 하나, 더이상 갈 곳이 없기 때문에 이 값을 variable3에 집어 넣는다.

 

let variable1 = null;
let variable2 = null;
let variable3;
let variable4 = variable1 || variable2 || variable3;

console.log(variable4 === undefined); //true

똑같은 이유로 variable4에는 맨 마지막 variable3의 값이 대입된다.


3. iterator와 generator

 

iterator란 객체를 next 메서드로 순환 할 수 있는 객체이다.

 

iterator는 next() 메소드를 가지고 있고, next 메소드는 아래의 규칙에 따라 구현되어야 한다.

 

    1. next 메소드는 arguments가 없다.

    2. next 메소드의 반환자는 done:boolean 과 value:any를 포함하는 object를 반환해야 한다.

    3. next 메소드이 반복이 끝날 때 done은 true를 반환해야 한다.

    4. 메소드 반복이 끝난 이후에도 계속 next를 호출 할 수 있다.(사실상 기능이 없기 때문에 호출 할 수만 있다.)

 

하나의 예를 보자면,

const book = [
  "Twinkle, twinkle, little bat!",
  "how I wonder whay you're",
  "Up above the world you fly",
  "Like a tea tray in the sky",
  "Twinkle, twinkle, little bat",
  "How I wonder what you're"
];

const it1 = book.values();
it1.next();  // {value: "Twinkle, twinkle, little bat!", done: false}
it1.next();  // {value: "how I wonder whay you're", done: false}
it1.next();  // {value: "Up above the world you fly", done: false}
it1.next();  // {value: "Like a tea tray in the sky", done: false}
it1.next();  // {value: "Twinkle, twinkle, little bat", done: false}
it1.next();  // {value: "How I wonder what you're", done: false}
it1.next();  // {value: undefined, done: true}
it1.next();  // {value: undefined, done: true}

book 배열에 values 메서드를 사용하여 이터레이터 객체로 만들었다.

 

이런 배열이 아닌 객체의 경우에도 이터러블 객체로 만들 수 있다. 이 일을 해주는 것은 이터레이션 프로토콜(이터러블/이터레이터 프로토콜)이라 한다.

 

class Log {
  constructor() {
    this.messages = [];
  }
  add(message) {
    this.messages.push({ message, timestamp: Date.now() });
  }
  [Symbol.iterator]() {
    return this.messages.values();
  }
}

const log = new Log();
log.add("first day at sea");
log.add("spotted whale");
log.add("spotted another vessel");

for (let entry of log) {
  console.log(`${entry.message}@${entry.timestamp}`);
}

이 예제에서 볼 수 있듯이 [Symbol.iterator] 라는 메소드를 통해 이터러블 객체화 시킨다. 이터러블 객체가 되었기 때문에 for..of를 통해 log 인스턴스를 배열처럼 순회할 수 있게 되었다. 


<<<<<<<<<<<<<<<<<<<<몰랐던 단어들에 대한 정리>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

 

객체(object)와 인스턴스(instance)의 차이

 

=> 인스턴스라는 단어가 나와서 무슨 뜻인지 몰라 찾아봤다. 예를 들어 티코, 마티즈와 같은 자동차들이 있다면 객체는 티코, 마티즈가 되고 객체들이 공통적으로 가질 수 있는 상태 정보와 행동에 대해 정의해 놓은 것을 클래스(자동차)라고 한다. 

 

이때 이 두 개 사이의 관계에 대한 정의가 객체와 인스턴스 인데

티코 마티즈 => 객체

티코는 자동차 클래스의 인스턴스!

마티즈는 자동차 클래스의 인스턴스! 

 

②이터러블과 이터레이터 (error404.co.kr/dev/2020/05/16/02.functional_es06/)

 

이터러블(Iterable) - 순회 가능한 객체를 가르키는 말. for of는 대부분 배열의 순회에 사용 된다고 알고 있지만 사실 순회가능(iterable)한 모든 객체를 대상으로 사용할 수 있다.

 

이터레이터(Iterator) - 위에 서 말한 이터러블한 객체가 [Symbol.interator]() 메소드로 반환하는 객체를 이터레이터 라고 한다. 이터레이터는 next() 메소드와 done, value 객체을 가진 객체이고 이 스펙을 이터레이터 프로토콜이라고 한다.

 

정리

  • 이터러블 : 이터레이터를 반환하는 [Symbol.iterator]()를 가진 값.
  • 이터레이터 : { value, done } 객체를 리턴하는 next()를 가진 값.
  • 이터러블/이터레이터 프로토콜 : 이터러블과 for of 등과 함께 사용할 수 있도록한 규약

③ 이터러블과 유사 배열

 

    이터러블 => Symbol.iterator가 구현된 객체(for..of를 사용할 수 있음)

    유사 배열 => 숫자 인덱스와 length 프로퍼티가 있어서 배열처럼 보이는 객체

 

let arrayLike = { // 인덱스와 length프로퍼티가 있음 => 유사 배열
  0: "Hello",
  1: "World",
  length: 2
};

이런 모양이 유사배열!

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


제네레이터란 이터레이터를 사용해 자신의 실행을 제어하는 함수. 제네레이터가 일반 함수와 다른 점은 2가지!

 

    1. 제네레이터는 언제든 호출자에게 제어권을 넘길(yield) 수 있다.

    2. 제너레이터는 호출한 즉시 실행되지 않는다. 대신 이터레이터를 반환하고, 이터레이터의 next 메서드를 호출함에         따라 실행된다.

 

무지개의 색깔들을 출력하는 제너레이터와 그에 대한 호출! (러닝 자바스크립트 p.266)

 

function* rainbow() {
  yield "red";
  yield "orange";
  yield "yellow";
  yield "green";
  yield "blue";
  yield "indigo";
  yield "violet";
}

const it = rainbow();
console.log(it);
console.log(it.next()); // {value: "red", done: false}
console.log(it.next()); // {value: "orange", done: false}
console.log(it.next()); // {value: "yellow", done: false}
console.log(it.next()); // {value: "green", done: false}
console.log(it.next()); // {value: "blue", done: false}
console.log(it.next()); // {value: "indigo", done: false}
console.log(it.next()); // {value: "violet", done: false}
console.log(it.next()); // {value: undefined, done:true}

for(let color of rainbow()){
  console.log(color);
}

 

rainbow()를 통해 제네레이터를 호출하고 it는 이터레이터 객체를 얻는다. 그 객체의 콘솔

 

이터레이터 객체의 next 메소드를 통해 다음 yield로 넘어간다. 이터레이터이기에 {value, done}의 객체를 반환한다.

이터레이터 객체이기에 for..of loop를 사용할 수 있다.

 

이런 제네레이터가 중요한 이유는 바로 함수의 바깥과 함수의 내부가 소통을 할 수 있다는 점 때문이다!!

 

소통의 과정을 보기 위해 하나의 예제를 조금 자세히 살펴보는게 좋다고 생각했다. 

 

function* interrogate() {
  let name = yield "What is your name?";
  let color = yield "What is your favorite color?";
  return `${name}'s favorite color is ${color}.`;
}

let it = interrogate();
console.log(it.next());
console.log(it.next("dongkyun"));
console.log(it.next("red"));

이 3개의 콘솔의 내용은 다음과 같다.

 

① 제네레이터 호출을 통해 it는 이터레이터 객체를 받는다.

 

② 첫번째 it.next()가 실행된다. 내 개인적으로 next의 실행에서 2가지의 규칙을 정해놓고 바라보면 제네레이터를 이해하기가 편한 것 같다. 첫째, 우측에서 좌측으로 간다. 둘째, yield 혹은 return에서 멈추고 함수 밖으로 나간다. 첫번째 next가 실행되다가 만나는 첫번째 yield 혹은 return은 "What is your name?" 좌측의 yield 이다. 이 내용은 첫번째 콘솔에서 확인이 가능하다.

 

③ 두번째 it.next('dongkyun')가 실행된다. 그 이전에 "What is your name?" 왼쪽 yield까지 실행되었기 때문에 it.next('dongkyun')의 실행범위는 첫번째 yield의 좌측 부분(let name = )과 "What is your favorite color?"의 왼쪽 yield까지이다. 이 때문에 괄호속에 넣은 dongkyun이라는 string이 name이라는 변수에 들어가게 된다.

 

④ 그 이후의 red 입력 또한 3번과 동일하다.

 

이 제너레이터에서 주의해야할 점은 2가지

  • 제네레이터는 화살표 표기법으로 만들 수 없으며 반드시 function*을 써야 한다.
  • 제네레이터에서 중요한 값을 return으로 반환하면 안된다. 반환하는 값을 사용해야 할 때는 yield를 써야 하고 return은 제네레이터응 중간에 종료하는 목적으로만 사용해야한다.

이렇듯 제네레이터는 함수와 호출자 사이의 양방향 통신을 가능하게 해준다. 제네레이터는 원래 동기적인 성격을 가졌지만, 프라미스와 연결하면 비동기로 사용할 수 있다.

 

https://poiemaweb.com/es6-generator

이런 식으로 제네레이터를 비동기로 사용한다.

 

솔직히 맨마지막 사진과 같은 코드는 정확히 이해하지도, 어느 경우에 필요한지도 잘 모르겠다. 정말 이런 스킬이 필요해져서 공부하는게 아닌 이상 이 부분을 제대로 이해하는 것은 어렵다고 생각한다. 일단, 이정도만 정리하고 나중에 필요해져서 공부를 할 때에 더 자세히 정리하도록 하자.


4. 커링

 

커링을 사용하는 가장 큰 이유는 재사용성! 

 

frontsom.tistory.com/10

 

커링 (Currying)

커링(Currying) 커링 은 함수 하나가 n개의 인자를 받는 과정을 n개의 단일 인자 함수열로 만드는 것 을 말하며, 특히 함수를 재사용 할 때 유용하게 쓰이는 JavaScript의 함수형 프로그래밍 기법 중 하

frontsom.tistory.com

이곳에 예제와 내용들이 잘 정리되어 있다.

 

간단히 예만 보자면 이런 식의 예를 들 수 있다.

 function multiplyThree1(x,y,z){
   console.log(x*y*z);
 }

 // 커링 함수
 function multiplyThree2(x) {
     // 클로저로 생성된 공간
     return function(y) {
         return function(z) {
             console.log(x * y * z);
         };
     };
 }

 // arrow function을 이용한 커링 함수
 let multiplyThree3=x=>y=>z=>{
   console.log(x*y*z);
 }

 multiplyThree1(4,5,2); //40
 multiplyThree2(4)(5)(2); // 40
 multiplyThree3(4)(5)(2); //40

5. lexical scope vs dynamic scope

 

자바스크립트는 lexical scope(static scope)를 따른다.

lexical scope는 함수 선언 위치에 따라 스코프가 결정!
dynamic scope는 함수의 호출 위치에 따라 스코프가 결정!

요즘의 대부분의 프로그래밍 언어들은 lexical scope를 사용한다.

 

이 스코프를 통해 실행 컨텍스트를 확인할 수 있다(스코프에는 전역 스코프와 지역 스코프가 존재한다. 함수 스코프가 지역 스코프의 전형적인 예.).

 

 var x='전역 변수';

 function outFunc(){
  
   var x='지역 변수';

   function inFunc(){
     console.log(x);
   }
   inFunc();
 }

 outFunc();

출처: m.blog.naver.com/PostView.nhn?blogId=gi_balja&logNo=221267355483&proxyReferer=https:%2F%2Fwww.google.com%2F

자바스크립트는 lexical scope를 사용하고 이로 인해 함수의 선언 위치에 따라 스코프가 결정되면서 위와 같은 실행 컨텍스트가 만들어진다. 

 

inFunc 함수에서 x 값을 콘솔에 찍는데 과연 '전역 변수' 가 찍힐 것이냐 아니면 '지역 변수' 가 찍힐 것이냐 헷갈릴 수 있다. 하지만, 이런 혼동은 실행 컨텍스트를 알면 해결할 수 있다. inFunc 함수는 x 값이 필요한데 inFunc 함수 속에 x 값이 없기 때문에 스코프 체인을 따라 onFunc 함수의 실행 컨텍스트로 온다. 이곳에 x 값이 있기 때문에 inFunc 함수의 x 콘솔에는 '지역 함수' 가 찍히게 된다.

 

var x='전역 변수';

function outFunc(){
  
  function inFunc(){
    console.log(x);
  }
  inFunc();
}

outFunc();

만약, 이전 코드와는 딱 한줄만이 차이나는 이런 경우가 있다고 생각해보자. 이때 inFunc 함수의 x 콘솔에서 '전역 변수' 가 찍힐 수 있는 이유는 inFunc 함수에 x의 값이 존재하지 않기 때문에 스코프 체인을 타고 outFunc 실행 컨텍스트로 온다. 하지만 이곳에서도 x의 값이 없기 때문에 다시 스코프 체인을 타고 전역 실행 컨텍스트로 온다. 그리고 이곳에 있는 x 값('전역 변수')을 사용하기 때문이다.

 

사실 이 예제는 충분히 예측 가능한 예제였기에 큰 혼동이 없었다.

 

다음 예제는 이 예제이다.

 

 var x = "전역 변수";

 function outFunc() {
   var x = "지역 변수";
   inFunc();
 }

 function inFunc() {
   console.log(x);
 }

 outFunc();

 나는 처음 이 코드를 봤을 때 '지역 변수'가 콘솔에 찍힐 줄 알았다. 하지만, 콘솔에 찍힌 내용은 '전역 변수' 였다. 이 또한, 실행 컨텍스트를 그려보면 쉽게 파악이 가능해진다.

 

출처: m.blog.naver.com/PostView.nhn?blogId=gi_balja&logNo=221267355483&proxyReferer=https:%2F%2Fwww.google.com%2F

전역 변수라는 string 값을 가진 x라는 변수, outFunc 함수, inFunc 함수 이 3개는 전역 실행 컨텍스트에 저장된다. 그 다음에 outFunc 함수와 inFunc 함수에 대한 자세한 내용이 다음 레벨의 실행 컨텍스트에서 저장된다. 

outFunc 함수 속 inFunc() 함수가 호출되는 순간 inFunc 실행 컨텍스트에 오게 된다. 하지만, 이 실행 컨텍스트 내부에는 x의 값이 존재하지 않는다. 따라서, x 값을 찾기 위해 스코프 체인을 타고 상위 레벨로 올라가야 하는데 이 상위 레벨은 이전의 경우와는 달리 onFunc 실행 컨텍스트가 아닌 전역 실행 컨텍스트이다. onFunc 실행 컨텍스트와 inFunc 실행 컨텍스트는 같은 레벨에 위치하기 때문에 스코프 체인을 타고 갈 수 없는 곳이다.


 

 

'MOFE 스터디' 카테고리의 다른 글

Week6(JavaScript)  (0) 2020.10.28
Week5(Javascript)  (0) 2020.10.17
Week4(Javascript)  (0) 2020.10.11
Week2(Javascript)  (0) 2020.09.24
Week1(Javascript)  (0) 2020.09.15

관련글 더보기

댓글 영역