[TIL] 2020 08 05 Wed (8일차)

Hailey Song·2020년 8월 5일
0

TIL_CodeStates

목록 보기
9/68

오늘 뭐야... 역대급으로 개념이 많아....

1. Scope

의미

: 범위 (어디까지가 잘 작동되는 코드인지 파악할 수 있음)
: 변수 접근 규칙에 따른 유효 범위 (변수와 그 값이 어디서부터 어디까지 유효한지 판단하는 범위)
: JavaScript는 기본적으로 함수가 선언되는 동시에 자신만의 Scope를 가짐.

적용범위 (Local Scope vs Global Scope)

안쪽(local Scope)에서 선언한 변수는 밖에서 사용할 수 없다.

let globalVar = 'Outside';
function local () {
  let localVar = 'Inside';
}

Console.log(globalVar) // 'Outside'
localVar // ReferrenceError

주요 규칙

1) Local Scope vs Global Scope

  • Scope 중첩 가능 : 함수 안에 함수를 넣을 수 있다.

  • Global Scope는 최상단의 Scope

  • 전역변수는 어디든 접근이 가능

  • 지역변수는 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다.

let greeting = 'hello';

function newGreeting() {
  let greeting = 'Good Evening' // 함수 내 새로운 선언으로 인식됨. 전역변수와 관련이 없음
  console.log(greeting);
}

console.log(greeting); // 'hello'
newGreeting(); // 'Good Evening'
console.log(greeting); // 'hello'

그런데 만약 함수 안에 새로운 선언이 없다면?

let greeting = 'hello';

function newGreeting() {
  greeting = 'Good Evening' // 선언(let)이 없음 -> 전역변수를 가져옴
  console.log(greeting);
}

console.log(greeting); // 'hello'
newGreeting(); // 'Good Evening'
console.log(greeting); // 'Good Evening'
 

2) block level vs. function level

Block : 중괄호 { }로 시작하고 끝나는 단위.

  • 특정한 block 내에 Scope가 한정되어 있다.
  • function level > block level

3) let vs var의 차이

JavaScript는 함수 단위로 자신만의 Scope를 가짐.

변수를 정의하는 키워드
var : block 범위를 벗어나도 같은 function Scope에서는 사용이 가능 -> function level

  • 재선언 가능
    let : block 단위로 Scope를 구분했을 때 예측하기 쉬운 코드를 작성할 수 있음 -> block level
  • 재선언 불가능(SyntaxError)
// var을 사용했을 때
for (var i = 0; i < 5; i++) {
  console.log(i);
}
console.log(i) // 5 
// for은 함수가 아니기 때문에 같은 function 내에서 변수를 공유함
// 재사용 위험성

// let을 사용했을 때
for (let i = 0; i < 5; i++) {
  console.log(i);
}
console.log(i) // Error 
// block 단위로만 사용 가능

상수를 정의하는 키워드
const : let과 동일하게 Block Scope를 따름. 값 재정의 시 TypeError

4) 전역 객체 Window

window : 전역 범위를 대표하는 객체

Glbal Scope에서 선언된 함수 Window와 연결됨
: window는 하나의 큰 객체 -> 선언된 함수는 윈도우 객체의 key로 연결됨

function imIn() {
  console.log('oh!');
}

console.log(imIn === window.imIn) // true

var로 선언된 전역 변수 역시 window와 연결됨

var metoo = 'my God!'

console.log(metoo === window.metoo) // true

let은 윈도우 객체에 담기지 않음

Strict Mode : 'use strict'
문법적으로 실수할 수 있는 부분들을 에러로 판단
키워드 없이 변수를 선언한 경우 자동으로 var 처리가 되므로, var로 인해 발생하는 실수를 방지해 줌

  • 선언 없이 전역 변수를 초기화하지 말 것!

2. Closure

의미

외부 함수의 변수에 접근할 수 있는 내부 함수
혹은 이러한 작동 원리를 일컫는 용어

function imOut() {
  let letMeIn = "It's cold outside."
  
  function imIn() { // 이 inIm 함수를 클로저 함수라고 부른다.
    let letMeOut = "It's boring!
    letMeIn = "I'm not borging!" // 외부 함수인 imOut의 변수를 사용할 수 있다.
    console.log(letMeIn); // 외부 함수인 imOut의 변수를 사용할 수 있다.
    console.log(letMeOut);
  }
  
  return imIn; // 함수 실행x 
}

클로저 함수 안에서는 지역 변수, 외부 함수의 변수, 전역 변수의 접근이 가능

유용하게 쓰이는 코딩 패턴

1) 커링 Currying

함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법
여러 개의 인자를 가진 함수를 호출 할 경우, 파라미터의 수보다 적은 수의 파라미터를 인자로 받으면 누락된 파라미터를 인자로 받는 기법
(Currying 뜻 찾아봤는데 사람 이름이었음..)

function normal(x,y) {
  return x + y;
}
normal(2,3) // 5

function currying(x) {
  function inCurrying(y) {
    return x + y;
  }
}
currying(2)(3) // 5

위의 예제에서 normal()과 currying()은 같은 작동을 하는 함수지만 currying의 경우 첫 번째 파라미터의 값을 고정해놓고 템플릿과 같이 재사용할 수 있다!
(코드스테이츠의 htmlMaker -> divMaker, spanMaker 등의 응용 너무 신박하다!)

function currying(x) {
  function inCurrying(y) {
    return x + y;
  }
}

let curryingTwo = currying(2);

curryingTwo(3) // 5
curryingTwo(8) // 10

참고 : https://sujinlee.me/currying-in-functional-javascript/

2) 클로저 모듈 패턴

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

  • 변수를 바깥에서 수정하지 못하게 만든다. 변수 변경에 제한이 필요할 때 직접 변수를 건드리지 않고 클로저 함수를 통해 간접적으로만 조절할 수 있게 만듦.
  • 그렇게 각각의 함수는 서로에게 영향을 끼치지 않음. 각각 같은 변수를 독립적으로 가지고 있기 때문에 변수의 재사용이 가능해짐.

어떤 개념인지 이해는 가는데.. 예제를 만들 정도로 이해한 건 아니라서..ㅠㅠ 다음 링크를 참고한 후 완전히 내 것으로 만들면 코드블럭을 생성하는 걸로..

참고 : https://asfirstalways.tistory.com/234

일단 내가 이해한 내용은 이렇다.

  1. 함수를 선언하고 안에 변수(혹은 '변경을 통제할' 변수)를 선언한다.
  2. 리턴에 객체{}를 넣는다. 키에 ??(이걸 뭐라고 하지??)를 넣고, 값에 함수를 넣어 변수 값을 조절한다. 객체에는 꼭 리턴이 들어간 함수가 있어야 한다!
  3. 함수.키()를 실행하면 값에 넣은 함수가 실행되어 변수값이 조절된다. 이때도 리턴이 들어간 명령어를 꼭 넣어주자..
  4. 함수.키()를 다른 변수에 할당하면 독립적으로 활용할 수 있다!

hmm.. 일단 짜볼까..

// 응악 모르겠다.. 코드스테이츠와 다른 예제를 쓰고 싶은데 아이디어가 생각이 안나..
let musicalKey = ['도','레','미','파','솔','라','시'];

function makeMusic() {
  let keyNote = '';
 
  return {
    addKey: function(i) {
      keyNote += musicalKey[i];
    }, // 쉼표 처리 꼭 해주세요.. 이거 때문에 헤맴
    addSpace: function() {
      keyNote += " ";
    },
    getNote: function() {
      return keyNote;
    }
  }
} // function도 안닫았었다....ㅋㅋㅋㅋ

let airplane = makeMusic(); // 함수명이 아니라 배열명을 넣었닼ㅋㅋ 실수가 많네

airplane.addKey(2);
airplane.addSpace();
airplane.addKey(1);
airplane.addKey(0);
airplane.addKey(1);
airplane.addSpace();
airplane.addKey(2);
airplane.addKey(2);
airplane.addKey(2);
airplane.getNote(); // 원하는 결과값 : '미 레도레 미미미'

성공!!!!! 오ㅗ왕와아옴니와와와왐아!!!!!!!!!
한 10분 동안 쉼표랑 괄호랑 함수명이랑 씨름하다 왔다ㅋㅋㅋㅋㅋㅋ

암튼 이때 let으로 다른 변수(곡 제목)를 선언해서 makeMusic()을 할당해주면, airplane을 건드리지 않고 새로운 곡을 작성할 수 있다!

let schoolBell = makeMusic();

schoolBell.addKey(4); // 대소문자때문에 또 헤맸다....ㅎㅎㅎㅎㅎ
schoolBell.addKey(4);
schoolBell.addKey(5);
schoolBell.addKey(5);
schoolBell.addKey(4);
schoolBell.addKey(4);
schoolBell.addKey(2);
schoolBell.addSpace();
schoolBell.addKey(4);
schoolBell.addKey(4);
schoolBell.addKey(2);
schoolBell.addKey(2);
schoolBell.addKey(1);
schoolBell.getNote(); // 원하는 결과값 : '솔솔라라솔솔미 솔솔미미레'

우여곡절이 있었지만 암튼 성공!

코드스테이츠의 또 다른 예제에서는 변수가 '카드' 혹은 '현금'만 들어갈 수 있게 제한하기 위해 클로저 모듈 함수를 사용했었다.
그걸 활용해보면...

function toBeOrNotToBe() {
  let answer = 'to be';
  
  return {
    positiveResult: function() {
      answer = 'to be';
      return '죽느냐 사느냐';
    },
    negetiveResult: function() {
      answer = 'not to be';
      return '그것이 문제로다'
    }
  }
}

let myAnswer = toBeOrNotToBe();

myAnswer.positiveResult(); // '죽느냐 사느냐'

myAnswer.negetiveResult(); // '그것이 문제로다'

오.. 한번에 성공!
암튼 이러면 type의 변수에 '나는 모르게썽..'과 같은 제 3의 값이 할당되는 것을 막을 수 있다.

3. 객체지향 JavaScript

객체 지향 프로그래밍

하나의 모델이 되는 청사진(blueprint -> class)을 만들고
그 청 사진을 바탕으로 한 객체(object -> instance)를 만드는 프로그래밍 패턴

클래스, 객체지향을 쓰는 이유는 현실 세계에 있는 모델들을 코드(프로그래밍 모델)로 옮기고자 할 때 사용한다.

  • 클래스의 경우 함수 이름을 대문자로 정의

ES5 : class는 함수로 정의할 수 있다.

function Cat(breed, color, servant) {
  // 인스턴스가 만들어질 때 실행되는 코드
}

ES6 : class라는 키워드를 이용해서 정의할 수 있다.

class Cat() { // function의 자리에 class가 온다
  constructor(breed, color, servant) {
    // 인스턴스가 만들어질 때 실행되는 코드
  }
}

그리고 new 키워드를 통해 클래스의 인스턴스를 만들어낼 수 있다.

let noel = new Cat('scottish', 'yellow', 'seventhCat');
let yaTong = new Cat('koshort', 'tricolor', 'hahaha');

noel // Cat{}
yaTong // Cat{}

여기서 Cat()은 클래스이고,
noel, yaTong는 모두 Cat() 클래스의 인스턴스이다.
(사실 야통이의 품종을 몰라서 코숏을 넣었다..)

클래스에 속성과 메소드를 정의하고
인스턴스에서 사용!

속성 : Cat()의 특징, 속성 등 (이름이나 색깔, 품종, 집사 이름, 좋아하는 음식 등등)
메소드 : Cat()라는 클래스를 통해서 하고 싶은 액션(꾹꾹이, 골골송, 그루밍, 우다다하기, 키보드에 올라타기)

아까의 예제를 다시 가져와서 Cat()이라는 클래스에 속성을 정의해보자.

// ES5
function Cat(breed, color, servant) {
  this.breed = breed;
  this.color = color;
  this.servant = servant;
}

//ES6
class Cat() {
  constructor(breed, color, servant) {
    this.breed = breed;
    this.color = color;
    this.servant = servant;
  }
}

이번에는 Cat() 클래스에 메소드를 정의해보면,

// ES5
function Cat(breed, color, servant) {}
Cat.prototype.kneading = function() {
  // 꾹꾹이를 구현하는 코드
  return '꾹꾸꾸꾸꾺꾸꾸꾸꾸꾸ㅜㄲ꾺';
}
Cat.prototype.purring = function() {
  // 골골송을 구현하는 코드
  return '골고ㅗㅗ고ㅗ고고ㅗㄹ고고골';
}

//ES6
class Cat() {
  constructor(breed, color, servant) {}
  kneading() {
      // 꾹꾹이를 구현하는 코드
  }
  purring() {
      // 골골송을 구현하는 코드
  }
}

// 두 방식의 차이는 
// ES5 클래스 함수에는 속성을 정의하고, 메소드는 함수 밖에서 prototype으로 정의
// ES6 클래스 함수 안에 속성과 메소드 모두 정의하는 건가?

인스턴스에서의 사용

yaTong.color; // "tricolor"
yaTong.kneading(); // "꾹꾸꾸꾸꾺꾸꾸꾸꾸꾸ㅜㄲ꾺"

야통.crying(); // '이얏호응'이랑
노을.crying(); // '웅냥' 넣어보고 싶었는데
인스턴스는 따로 메소드를 구별해서 쓸 수는 없나? 흠... 이건 속성으로 정의되어야 하는건가?

야통.crying; // '이얏호응'이랑
노을.crying; // '웅냥'
이런 식으로??

그럼 Cat()이랑 인스턴스 모두 매개변수를 하나씩 더 추가해야 하는 상황이 생기는데
그건 그냥 객체 키값 추가해주듯이 하면 되나? 한 번 해볼까?ㅋㅋㅋㅋㅋ 이상한데에서 집요함..

시도했는데 ReferrenceError 뜸.
매개변수 추가하는 방법을 모르겠당..

function AnotherCat(breed, color, servant, crying) {
  Cat.call(this, breed, color, servant);
  this.crying = crying;
};

noel = new AnotherCat('scottish', 'yellow', 'seventhCat', '웅냥');
yaTong = new AnotherCat('koshort', 'tricolor', 'hahaha', '이얏호응');

noel.crying; // '웅냥'
yaTong.crying; // '이얏호응'

이렇게 하면 나오긴 하는데.. 그럼 AnotherCat이라는 클래스를 또 만들어야하니까 번거롭다. 애초에 속성의 매개변수 숫자는 변할 수 없는걸까?
흠.. 그래서 ES5와 ES6의 식이 다른걸까?

아니면 ...을 활용하면 매개변수의 숫자를 미리 정할 수 있지 않을까?

/// 실패한 코드 

let breed = "";
let color = "";
let servant = ""; // 각각에 변수 선언을 안 해줘서 ReferrenceError가 났다.

let property = [breed, color, servant] // 

function Cat(...property) {
  this.breed = breed;
  this.color = servant;
  this.servant = servant;
}

let noelProperty = ['scottish', 'yellow', 'seventhCat'];
let yaTongProperty = ['koshort', 'tricolor', 'hahaha'];

let noel = new Cat(...noelProperty);
let yaTong = new Cat(...yaTongProperty);

// 중간 점검
noel.color // ""가 나옴...이러시면 안됩니다...
yaTong.sevant //

//------ 암튼 중간 점검 결과 무시하고 원래 시도하고 싶었던 것.... 

property.push(crying);
noelProperty.push('웅냥');
yaTongProperty.push('이얏호응');

//dkddkdakkasdgkasldklasdgasdlg;askd암ㄴ인아망히낭미하ㅣㄷ자ㅣㅏ이ㅏㅣ망ㄴ

어렵다... 이건 좀더 생각해야 할 문제같음...ㅠㅠㅠㅠ 매개변수 개수 수정하는거....
함수를 재선언해주면 되나? 클래스 함수를 재선언하면 인스턴스 함수는 어떻게 될까??
급 궁금해짐

// 클래스 함수 선언
function Cat(breed, color, servant) {
  this.breed = breed;
  this.color = color;
  this.servant = servant;
}
// 인스턴스 
let noel = new Cat('scottish', 'yellow', 'seventhCat');
let yaTong = new Cat('koshort', 'tricolor', 'hahaha');

// 중간 확인
noel.breed // "scottish" 잘 들어감

// 클래스 함수 재선언
function Cat(breed, color, servant, crying) {
  this.breed = breed;
  this.color = color;
  this.servant = servant;
  this.crying = crying;
}

// 중간 확인2
noel.breed // "scottish"
noel.crying // undefined  << 아하 이런 식이군

// 인스턴스 재선언
let noel = new Cat('scottish', 'yellow', 'seventhCat', '웅냥')
let yaTong = new Cat('koshort', 'tricolor', 'hahaha', '이얏호응');

// 결과물
noel.crying // "웅냥" 성공
yaTong.crying // "이얏호응" 성공!

하... 웅냥과 이얏호응 듣기가 이렇게 어렵다니...

그리고 또 궁금한 거. 클래스 함수에 메소드도 넣어놓고 함수 재선언하면 메소드도 사라지나?

// 클래스 함수 선언
function Cat(name, breed, color, servant) {
  this.name = name;
  this.breed = breed;
  this.color = color;
  this.servant = servant;
}
 
Cat.prototype.introduce = function() {
  return `${this.name}이의 집사님은 ${this.servant}님입니다.`;
}

// 인스턴스 선언
let noel = new Cat('노을','scottish', 'yellow', 'seventhCat')
let yaTong = new Cat('야통', 'koshort', 'tricolor', 'hahaha');

// 중간 점검
yaTong.servant; // "hahaha"
yaTong.introduce(); //"야통이의 집사님은 hahaha님입니다."

// 클래스 함수 재선언
function Cat(name, breed, color, servant, crying) {
  this.name = name;
  this.breed = breed;
  this.color = color;
  this.servant = servant;
  this.crying = crying;
}

// 결과물
yaTong.introduce(); //"야통이의 집사님은 hahaha님입니다."

상관은 없네..ㅋㅋㅋ 메소드를 클래스 함수 밖에서 선언했기 때문에 가능한가 봄.

암튼 오늘 이것저것 많이 했는데.. 흠.. 역시 어렵다..


prototype? : 모델의 청사진을 만들 때 쓰는 원형 객체(original form)
constructor? : 인스턴스가 초기화될 때 실행하는 생성자 함수
this? : 함수가 실행될 때, 해당 scope마다 생성되는 고유한 실행 context(excution context). new 키워드로 인스턴스를 생성했을 때에는 해당 인스턴스가 바로 this의 값이 됨.

cf. 참고

let arr = [1,2,3]
let arr = new Array(1,2,3)

문법적으로 같은 것. arr는 Array의 인스턴스이다. Array는 클래스!

4. 매개변수 parameter

Parameter의 갯수가 유동적인 함수

아닠ㅋㅋㅋㅋ 앞에서 3시간동안 유동적인 매개변수 만들어보려고 그렇게 뻘짓했는데 오늘 하는 내용이었다...하........

1) ES6 : Rest parameter

...nums의 형태로 사용한다. 오 아까의 내 가설이 맞았긴 맞았네. 결실은 못 맺었지만.

function getMax(...nums) {
  console.log(nums);
}
getMax(3,4,8,10);

아하 이런 식으로..

2) ES5 : arguments

꼭 뒤에 s를 붙이자.
이 방법은 parameter 설정은 해주지 않고 함수 안에서 arguments를 이용한다.

function getMax() {
  console.log(arguments);
}
getMax(3,4,8,10);

이 arguments는 배열이 아닌 유사 배열. 배열의 메소드를 사용할 수 없음.

3) Default parameter

parameter을 디폴트로 할당할 수 있음.
문자열/숫자/객체 등 어떠한 타입도 가능.

function todaysWeather(morning, evening = 'cloudy') {
  return `아침은 ${morning}! 져녁은 ${evening}!`;
}

todaysWeather('sunny'); // "아침은 sunny! 져녁은 cloudy!"
todaysWeather('sunny', 'rainy'); // "아침은 sunny! 져녁은 rainy!"
todaysWeather('sunny', undefined); // "아침은 sunny! 져녁은 cloudy!"

4. Koans

Array method
reduce에 대해서

https://www.freecodecamp.org/news/reduce-f47a7da511a9/
https://dev-gp.tistory.com/19
https://steemit.com/javascript/@rouka/reduce

0개의 댓글